RemoteThread 远程线程 线程注入

基本原理是将dll的绝对路径使用VirtualAllocEx在目标进程中获取一块内存,然后将dll的绝对路径注入到这个新申请的区域,作为远程函数的参数。获取目标进程的kernel32.dll的模块地址,获取kernel32中LoadLibraryA函数的地址。然后使用CreateRemoteThread函数调用LoadLibraryA去加载我们自己想要注入的dll。至此完成了dll注入。

之所以选择使用dll注入自己想要允许的代码,是因为CreateRemoteThread这个远程函数只能调用,目标进程内所含有的函数,一般来说,是没有故意留出的接口的,并且所有进程都会加载kernel32这个模块,故此有了这种dll注入方法。

代码:

function.h
function.cpp
RemThreadInjector.h
RemThreadInjector.cpp
RemoteThread.cpp

function.h:

#pragma once

#include <string>
#include <string.h>
#include <Windows.h>

using namespace std;

char* wchar2char(WCHAR* data);
WCHAR* char2wchar(char* data);
wstring string2wstring(string str);
string wstring2string(wstring wstr);

function.cpp:

#include "function.h"

char* wchar2char(WCHAR* data)
{
    //获取缓冲区大小,并申请空间,缓冲区大小事按字节计算的  
    unsigned int char_len = WideCharToMultiByte(CP_ACP, 0, data, -1, NULL, 0, NULL, NULL);
    char* buffer = (char*)malloc(char_len + 1);
    //宽字节编码转换成多字节编码  
    WideCharToMultiByte(CP_ACP, 0, data, -1, buffer, char_len, NULL, NULL);
    buffer[char_len] = '\0';
    return buffer;
}

WCHAR* char2wchar(char* data)
{
    //获取缓冲区大小,并申请空间,缓冲区大小按字符计算  
    unsigned int wchar_len = MultiByteToWideChar(CP_ACP, 0, data, strlen(data), NULL, 0);
    unsigned int malloc_size = (wchar_len + 1) * 2;
    char* buffer = (char*)malloc(malloc_size);
    //多字节编码转换成宽字节编码  
    MultiByteToWideChar(CP_ACP, 0, data, -1, (WCHAR*)buffer, wchar_len);
    //添加字符串结尾  WCHAR 占用两个字节,所以最后的两个字节都要填充\0
    buffer[malloc_size - 2] = '\0';
    buffer[malloc_size - 1] = '\0';
    return (WCHAR*)buffer;
}

//将string转换成wstring
wstring string2wstring(string str)
{
    wstring result;
    //获取缓冲区大小,并申请空间,缓冲区大小按字符计算  
    unsigned int wchar_len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);
    TCHAR* buffer = new TCHAR[wchar_len + 1];
    //多字节编码转换成宽字节编码  
    MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, wchar_len);
    //添加字符串结尾  由于上边直接申请的TCHAR,就是WCHAR,直接是两个字节为单位的,所以直接将最后一位置为\0其实是将两个字节都置为了\0
    buffer[wchar_len] = '\0';
    //删除缓冲区并返回值  
    result.append(buffer);
    delete[] buffer;
    return result;
}

//将wstring转换成string  
string wstring2string(wstring wstr)
{
    string result;
    //获取缓冲区大小,并申请空间,缓冲区大小事按字节计算的  
    int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);
    char* buffer = new char[len + 1];
    //宽字节编码转换成多字节编码  
    WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), buffer, len, NULL, NULL);
    buffer[len] = '\0';
    //删除缓冲区并返回值  
    result.append(buffer);
    delete[] buffer;

    return result;
}

RemThreadInjector.h:

#pragma once

#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <string>
#include "function.h"

class CRemThreadInjector
{
public:
    CRemThreadInjector(const WCHAR * InjectDllPath);
    ~CRemThreadInjector();
    // 注入DLL到指定的进程空间
    BOOL InjectModuleInto(DWORD dwProcessId);
    // 从指定的进程空间卸载DLL
    BOOL EjectModuleFrom(DWORD dwProcessId);
protected:
    WCHAR wchar_DllName[MAX_PATH];
    char char_DllName[MAX_PATH];
    // 调整特权级别
    static BOOL EnableDebubgPrivilege(BOOL bEnable);
    static BOOL is_same_dll(const WCHAR* dll1_path, const WCHAR* dll2_path);
};

RemThreadInjector.cpp

#include "RemThreadInjector.h"



CRemThreadInjector::CRemThreadInjector(const WCHAR* InjectDllPath)
{
    memset(wchar_DllName, '\0', sizeof(WCHAR) * MAX_PATH);
    memset(char_DllName, '\0', sizeof(char) * MAX_PATH);
    wcsncpy_s(wchar_DllName, InjectDllPath, MAX_PATH);
    char* t_char = wchar2char(wchar_DllName);
    strncpy_s(char_DllName, t_char, MAX_PATH);
    free(t_char);
    EnableDebubgPrivilege(TRUE);
}
CRemThreadInjector::~CRemThreadInjector()
{
    EnableDebubgPrivilege(FALSE);
}

BOOL CRemThreadInjector::EnableDebubgPrivilege(BOOL bEnable)
{
    // 附给本进程特权,以便访问系统进程
    BOOL bOk = FALSE;
    HANDLE hToken;

    // 打开一个进程的访问令牌
    if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
    {
        // 取得特权名称为 “SetDebugPrivilege”的LUID
        LUID uID;
        ::LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &uID);

        // 调整特权级别
        TOKEN_PRIVILEGES tp;
        tp.PrivilegeCount = 1;
        tp.Privileges[0].Luid = uID;
        tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
        ::AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
        bOk = (::GetLastError() == ERROR_SUCCESS);

        // 关闭访问令牌句柄
        ::CloseHandle(hToken);
    }
    return bOk;
}

BOOL CRemThreadInjector::InjectModuleInto(DWORD dwProcessId)
{
    if (::GetCurrentProcessId() == dwProcessId)
        return FALSE;

    // 首先查看目标进程是否加载了这个模块
    BOOL bFound = FALSE;
    MODULEENTRY32 me32 = { 0 };
    HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
    me32.dwSize = sizeof(MODULEENTRY32);
    if (::Module32First(hModuleSnap, &me32))
    {
        do
        {
            if (is_same_dll(me32.szExePath, wchar_DllName))
            {
                bFound = TRUE;
                break;
            }
        } while (::Module32Next(hModuleSnap, &me32));
    }
    ::CloseHandle(hModuleSnap);

    // 如果能够找到,就不重复加载了(因为重复加载没有用,Windows只将使用计数加1.其他什么也不做)
    if (bFound)
        return FALSE;

    // 试图打开目标进程
    HANDLE hProcess = ::OpenProcess(PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION, FALSE, dwProcessId);
    if (hProcess == NULL)
        return FALSE;

    // 在目标进程中申请空间,存放字符串m_szDllName,作为远程线程的参数
    int cbSize = (strlen(char_DllName) + 1);
    LPVOID lpRemoteDllName = ::VirtualAllocEx(hProcess, NULL, cbSize, MEM_COMMIT, PAGE_READWRITE);
    if (lpRemoteDllName == NULL)
        return FALSE;
    ::WriteProcessMemory(hProcess, lpRemoteDllName, char_DllName, cbSize, NULL);

    // 取得LoadLibraryA 函数的地址,我们将以它作为远程线程函数启动
    HMODULE hModule = ::GetModuleHandle(LPCWSTR(TEXT("kernel32.dll")));
    if (hModule == NULL)
        return FALSE;
    LPTHREAD_START_ROUTINE pfnStartRoutine = (LPTHREAD_START_ROUTINE)::GetProcAddress(hModule, "LoadLibraryA");

    // 启动远程线程
    HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, pfnStartRoutine, lpRemoteDllName, 0, NULL);

    if (hRemoteThread == NULL)
    {
        ::CloseHandle(hProcess);
        return FALSE;
    }

    // 等待目标线程运行结束,即LoadLibraryA函数返回
    ::WaitForSingleObject(hRemoteThread, INFINITE);
    ::CloseHandle(hRemoteThread);
    ::CloseHandle(hProcess);
    return TRUE;
}

BOOL CRemThreadInjector::EjectModuleFrom(DWORD dwProcessId)
{
    if (::GetCurrentProcessId() == dwProcessId)
        return FALSE;

    // 首先查看目标进程是否加载了这个模块
    BOOL bFound = FALSE;
    MODULEENTRY32 me32 = { 0 };
    HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
    me32.dwSize = sizeof(MODULEENTRY32);
    if (::Module32First(hModuleSnap, &me32))
    {
        do
        {
            if (is_same_dll(me32.szExePath, wchar_DllName))
            {
                bFound = TRUE;
                break;
            }
        } while (::Module32Next(hModuleSnap, &me32));
    }
    ::CloseHandle(hModuleSnap);

    // 如果找不到就返回出错
    if (!bFound)
        return FALSE;

    // 试图打开目标进程
    HANDLE hProcess = ::OpenProcess(PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION, FALSE, dwProcessId);
    if (hProcess == NULL)
        return FALSE;

    // 取得FreeLibrary 函数的地址,我们将以它作为远程线程函数启动
    HMODULE hModule = ::GetModuleHandle(LPCWSTR(TEXT("kernel32.dll")));
    if (hModule == NULL)
        return FALSE;
    LPTHREAD_START_ROUTINE pfnStartRoutine = (LPTHREAD_START_ROUTINE)::GetProcAddress(hModule, "FreeLibrary");

    // 启动远程线程
    HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, pfnStartRoutine, me32.hModule, 0, NULL);

    if (hRemoteThread == NULL)
    {
        ::CloseHandle(hProcess);
        return FALSE;
    }

    // 等待目标线程运行结束,即LoadLibraryA函数返回
    ::WaitForSingleObject(hRemoteThread, INFINITE);
    ::CloseHandle(hRemoteThread);
    ::CloseHandle(hProcess);
    return TRUE;
}

BOOL CRemThreadInjector::is_same_dll(const WCHAR* dll1_path, const WCHAR* dll2_path)
{
    WCHAR dll1_path_copy[MAX_PATH], dll2_path_copy[MAX_PATH];
    wcsncpy_s(dll1_path_copy, dll1_path, MAX_PATH);
    wcsncpy_s(dll2_path_copy, dll2_path, MAX_PATH);

    int flag = 1;
    int i = 0;
    while (i < MAX_PATH && dll1_path_copy[i] != WCHAR('\0') && dll2_path_copy[i] != WCHAR('\0'))
    {
        if (dll1_path_copy[i] != dll2_path_copy[i] && ((dll1_path_copy[i] != '\\' && dll1_path_copy[i] != '/') || (dll2_path_copy[i] != '\\' && dll2_path_copy[i] != '/')))
        {
            flag = 0;
            break;
        }
        i++;
    }
    return flag;
}

RemoteThread.cpp

// RemoteThread.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "RemThreadInjector.h"
#include <iostream>
#include <string>

using namespace std;

int FindPID(string ProcessName);


int FindPID(string ProcessName)
{
    int pid = -1;
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(pe32);
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE) {
        cout << "CreateToolhelp32Snapshot Error!" << endl;;
        return false;
    }
    BOOL bResult = Process32First(hProcessSnap, &pe32);
    int num(0);
    while (bResult)
    {
        if (wstring2string(wstring(pe32.szExeFile)) == ProcessName)
        {
            pid = pe32.th32ProcessID;
            break;
        }
        bResult = Process32Next(hProcessSnap, &pe32);
    }
    CloseHandle(hProcessSnap);
    return pid;
}


LPCWSTR stringToLPCWSTR(const std::string& s) {
    size_t convertedChars = 0;
    std::string curLocale = setlocale(LC_ALL, NULL);   //curLocale="C"
    setlocale(LC_ALL, "chs");
    const char* source = s.c_str();
    size_t charNum = sizeof(char) * s.size() + 1;
    wchar_t* dest = new wchar_t[charNum];
    mbstowcs_s(&convertedChars, dest, charNum, source, _TRUNCATE);
    setlocale(LC_ALL, curLocale.c_str());
    return dest;
}

int main()
{
    wstring injectDllPath = TEXT("D:/c++/vs_data/create_ce_dll/x64/Release/create_ce_dll.dll");
    CRemThreadInjector thread_injector(injectDllPath.c_str());
    string inject_process_name = "YuanShen.exe";
    //string inject_process_name = "notepad.exe";
    int pid = FindPID(inject_process_name);

    bool inject_flag = FALSE;
    string info = "";

    inject_flag = thread_injector.InjectModuleInto(pid);
    info = "inject " + inject_process_name + " " + to_string(pid) + " " + to_string(inject_flag) + "\n";
    printf("%s", info.c_str());

    getchar();

    inject_flag = thread_injector.EjectModuleFrom(pid);
    info = "eject " + inject_process_name + " " + to_string(pid) + " " + to_string(inject_flag) + "\n";
    printf("%s", info.c_str());
    return 0;
}


文章目录