要实现线程的远程注入必须使用Windows提供的CreateRemoteThread函数来创建一个远程线程

该函数的原型如下:

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

参数说明:

hProcess:目标进程的句柄

lpThreadAttributes:指向线程的安全描述结构体的指针,一般设置为NULL,表示使用默认的安全级别

dwStackSize:线程堆栈大小,一般设置为0,表示使用默认的大小,一般为1M

lpStartAddress:线程函数的地址

lpParameter:线程参数

dwCreationFlags:线程的创建方式

CREATE_SUSPENDED 线程以挂起方式创建

lpThreadId:输出参数,记录创建的远程线程的ID

CreateRemoteThread函数介绍完毕,其他详细信息参考MSDN中关于该函数的详细说明!

既然知道了使用这个函数来创建一个远程线程,接下来我们就来定义线程函数体,和普通的线程函数的

定义相同,远程线程的线程函数必须定义程类的静态成员函数或者全局函数

例如:

DWORD __stdcall threadProc(LPVOID lParam)
{
//我们在这里先将该线程函数定义为空函数
return 0;
}

在这里我们先将线程函数体定义为空,因为要作为远程注入的线程,线程函数体的编写方式和普通线程函数

稍有不同。

然后将线程代码拷贝到目标进程地址空间中(该地址必须是页面属性为PAGE_EXECUTE_READWRITE的页面)或者

其他宿主进程能执行地方(如:共享内存映射区)。在这里我们选择宿主进程。在拷贝线程体的时候我们需要

使用VirtualAllocEx函数在宿主进程中申请一块存储区域,然后再通过WriteProcessMemory函数将线程代码写

入宿主进程中。

要取得宿主进程的ID可以有很多种方法可以使用Psapi.h中的函数,也可以使用Toolhelp函数,在这里提供一种

使用Toolhelp实现的函数,函数如下

//根据进程名称得到进程的ID,如果有多个实例在同时运行的话,只返回第一个枚举到的进程ID
DWORD processNameToId(LPCTSTR lpszProcessName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(hSnapshot, &pe)) {
MessageBox(NULL,
"The frist entry of the process list has not been copyied to the buffer",
"Notice", MB_ICONINFORMATION | MB_OK);
return 0;
} while (Process32Next(hSnapshot, &pe)) {
if (!strcmp(lpszProcessName, pe.szExeFile)) {
return pe.th32ProcessID;
}
} return 0;
}

以上步骤完成之后就可以使用CreateRemoteThread创建远程线程了!示例代码如下

#include <windows.h>
#include <TlHelp32.h>
#include <iostream> //要插入宿主进程中的线程函数
DWORD __stdcall threadProc(LPVOID lParam)
{
return 0;
} int main(int argc, char* argv[])
{
const DWORD dwThreadSize = 4096;
DWORD dwWriteBytes; std::cout << "Please input the name of target process" << std::endl;
char szExeName[MAX_PATH] = { 0 };
//等待输入宿主进程名称
std::cin >> szExeName; //得到指定名称进程的进程ID,如果有多个进程实例,则得到第一个进程ID
DWORD dwProcessId = processNameToId(szExeName);
HANDLE hTargetProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId)
void* pRemoteThread = VirtualAllocEx(hTargetProcess, 0,
dwThreadSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//把线程体写入宿主进程中
if (!WriteProcessMemory(hTargetProcess,
pRemoteThread, &threadProc, dwThreadSize, 0)) {
MessageBox(NULL, "Write data to target process failed !",
"Notice", MB_ICONINFORMATION | MB_OK);
return 0;
}
//在宿主进程中创建线程
HANDLE hRemoteThread = CreateRemoteThread(
hTargetProcess, NULL, 0, (DWORD (__stdcall *)(void *))pRemoteThread,
NULL, 0, &dwWriteBytes);
if (!hRemoteThread) {
MessageBox(NULL, "Create remote thread failed !", "Notice", MB_ICONSTOP);
return -1;
}
return 0;
}

当上面的代码运行的时候会在宿主进程中创建一条由程序员定义的线程,只不过现在这个线程函数体为空

什么都不做。

下面我们来编写具体的线程函数体的内容,在这里我们只是简单的显示一个消息对话框MessageBox

修改之后的线程函数体如下:

DWORD __stdcall threadProc(LPVOID lParam)
{
MessageBox(NULL, "hello", "hello", MB_OK);
return 0;
}

线程体修改完毕之后我们运行程序,将线程注入到宿主进程之中。不过此时会产生一个非法访问的错误。原

因就是线程体中的MessageBox(NULL, "hello", "hello", MB_OK);函数的第二和第三个参数所指向的字符串

是存在于当前进程的地址空间中,宿主进程中的线程访问该字符串"hello"就会出现访问内存非法的错误。

解决的方法就是将该字符串的内容也拷贝到宿主进程的地址空间中,而且连同MessageBox函数在User32.dll

中的地址也拷贝到宿主进程之中。

要将字符串和MessageBox函数的入口地址拷贝到宿主进程中我们首先定义下面这个RemoteParam结构体,用来

存放MessageBox函数的入口地址和MessageBox显示的字符串的内容,该结构的定义如下:

//线程参数
typedef struct _RemoteParam {
char szMsg[12]; //MessageBox函数显示的字符串
DWORD dwMessageBox;//MessageBox函数的入口地址
} RemoteParam, * PRemoteParam; RemoteParam remoteData;
ZeroMemory(&remoteData, sizeof(RemoteParam)); HINSTANCE hUser32 = LoadLibrary("User32.dll");
remoteData.dwMessageBox = (DWORD)GetProcAddress(hUser32, "MessageBoxA");
strcat(remoteData.szMsg, "Hello/0"); //在宿主进程中分配存储空间
RemoteParam* pRemoteParam = (RemoteParam*)VirtualAllocEx(
hTargetProcess , 0, sizeof(RemoteParam), MEM_COMMIT, PAGE_READWRITE); if (!pRemoteParam) {
MessageBox(NULL, "Alloc memory failed !",
"Notice", MB_ICONINFORMATION | MB_OK);
return 0;
} //将字符串和MessageBox函数的入口地址写入宿主进程
if (!WriteProcessMemory(hTargetProcess ,
pRemoteParam, &remoteData, sizeof(remoteData), 0)) {
MessageBox(NULL, "Write data to target process failed !",
"Notice", MB_ICONINFORMATION | MB_OK);
return 0;
} //创建远程线程
HANDLE hRemoteThread = CreateRemoteThread(
hTargetProcess, NULL, 0, (DWORD (__stdcall *)(void *))pRemoteThread,
pRemoteParam, 0, &dwWriteBytes);

另外还需要注意的一点是,在打开进程的时候有些系统进程是无法用OpenProcess函数

打开的,这个时候就需要提升进程的访问权限,进而来达到访问系统进程的目的,在这里

我提供了一个提升进程访问权限的函数enableDebugPriv(),该函数的定义如下:

//提升进程访问权限
bool enableDebugPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp; if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
return false;
} if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)) {
CloseHandle(hToken);
return false;
} tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) {
CloseHandle(hToken);
return false;
} return true;
}

至此创建远程线程的工作全部结束,下面就给出完整的代码:

#pragma once
#include "stdafx.h"
#include <windows.h>
#include <TlHelp32.h>
#include <iostream> //线程参数结构体定义
typedef struct _RemoteParam {
char szMsg[12]; //MessageBox函数中显示的字符提示
DWORD dwMessageBox;//MessageBox函数的入口地址
} RemoteParam, * PRemoteParam; //定义MessageBox类型的函数指针
typedef int (__stdcall * PFN_MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, DWORD); //线程函数定义
DWORD __stdcall threadProc(LPVOID lParam)
{
RemoteParam* pRP = (RemoteParam*)lParam; PFN_MESSAGEBOX pfnMessageBox;
pfnMessageBox = (PFN_MESSAGEBOX)pRP->dwMessageBox;
pfnMessageBox(NULL, pRP->szMsg, pRP->szMsg, 0); return 0;
} //提升进程访问权限
bool enableDebugPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp; if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
return false;
} if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)) {
CloseHandle(hToken);
return false;
} tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) {
CloseHandle(hToken);
return false;
} return true;
} //根据进程名称得到进程ID,如果有多个运行实例的话,返回第一个枚举到的进程的ID
DWORD processNameToId(LPCTSTR lpszProcessName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(hSnapshot, &pe)) {
MessageBox(NULL,
"The frist entry of the process list has not been copyied to the buffer",
"Notice", MB_ICONINFORMATION | MB_OK);
return 0;
} while (Process32Next(hSnapshot, &pe)) {
if (!strcmp(lpszProcessName, pe.szExeFile)) {
return pe.th32ProcessID;
}
} return 0;
} int main(int argc, char* argv[])
{
//定义线程体的大小
const DWORD dwThreadSize = 4096;
DWORD dwWriteBytes;
//提升进程访问权限
enableDebugPriv(); //等待输入进程名称,注意大小写匹配
std::cout << "Please input the name of target process !" << std::endl;
char szExeName[MAX_PATH] = { 0 };
std::cin >> szExeName; DWORD dwProcessId = processNameToId(szExeName); if (dwProcessId == 0) {
MessageBox(NULL, "The target process have not been found !",
"Notice", MB_ICONINFORMATION | MB_OK);
return -1;
} //根据进程ID得到进程句柄
HANDLE hTargetProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (!hTargetProcess) {
MessageBox(NULL, "Open target process failed !",
"Notice", MB_ICONINFORMATION | MB_OK);
return 0;
} //在宿主进程中为线程体开辟一块存储区域
//在这里需要注意MEM_COMMIT | MEM_RESERVE内存非配类型以及PAGE_EXECUTE_READWRITE内存保护类型
//其具体含义请参考MSDN中关于VirtualAllocEx函数的说明。
void* pRemoteThread = VirtualAllocEx(hTargetProcess, 0,
dwThreadSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!pRemoteThread) {
MessageBox(NULL, "Alloc memory in target process failed !",
"notice", MB_ICONINFORMATION | MB_OK);
return 0;
} //将线程体拷贝到宿主进程中
if (!WriteProcessMemory(hTargetProcess,
pRemoteThread, &threadProc, dwThreadSize, 0)) {
MessageBox(NULL, "Write data to target process failed !",
"Notice", MB_ICONINFORMATION | MB_OK);
return 0;
}
//定义线程参数结构体变量
RemoteParam remoteData;
ZeroMemory(&remoteData, sizeof(RemoteParam)); //填充结构体变量中的成员
HINSTANCE hUser32 = LoadLibrary("User32.dll");
remoteData.dwMessageBox = (DWORD)GetProcAddress(hUser32, "MessageBoxA");
strcat(remoteData.szMsg, "Hello/0"); //为线程参数在宿主进程中开辟存储区域
RemoteParam* pRemoteParam = (RemoteParam*)VirtualAllocEx(
hTargetProcess , 0, sizeof(RemoteParam), MEM_COMMIT, PAGE_READWRITE); if (!pRemoteParam) {
MessageBox(NULL, "Alloc memory failed !",
"Notice", MB_ICONINFORMATION | MB_OK);
return 0;
} //将线程参数拷贝到宿主进程地址空间中
if (!WriteProcessMemory(hTargetProcess ,
pRemoteParam, &remoteData, sizeof(remoteData), 0)) {
MessageBox(NULL, "Write data to target process failed !",
"Notice", MB_ICONINFORMATION | MB_OK);
return 0;
} //在宿主进程中创建线程
HANDLE hRemoteThread = CreateRemoteThread(
hTargetProcess, NULL, 0, (DWORD (__stdcall *)(void *))pRemoteThread,
pRemoteParam, 0, &dwWriteBytes); if (!hRemoteThread) {
MessageBox(NULL, "Create remote thread failed !", "Notice", MB_ICONINFORMATION | MB_OK);
return 0;
} //等待线程执行结束
WaitForSingleObject(hRemoteThread,INFINITE); //释放开辟的存储
VirtualFreeEx(hTargetProcess,pRemoteThread,0,MEM_RELEASE);
VirtualFreeEx(hTargetProcess,pRemoteParam,0,MEM_RELEASE); //关闭
CloseHandle(hRemoteThread);
CloseHandle(hTargetProcess); return 0;
}

CreateRemoteThread创建远程线程的更多相关文章

  1. 远程线程注入方法CreateRemoteThread

    最近在整理学习Windows注入方面的知识,这个远程注入前面早写过,现在看看人家博客的理解整理,整理, 需要源码的可以到我的github上下载. 链接是  https://github.com/Ars ...

  2. 【windows核心编程】使用远程线程注入DLL

    前言 该技术是指通过在[目标进程]中创建一个[远程线程]来达到注入的目的. 创建的[远程线程]函数为LoadLibrary, 线程函数的参数为DLL名字, 想要做的工作在DLL中编写.  示意图如下: ...

  3. 实战DELPHI:远程线程插入(DLL注入)

    http://www.jx19.com/xxzl/Delphi/2010/04/17/ShiZhanDELPHI_YuanChengXianChengChaRu_DLLZhuRu/ 远程注入DLL方法 ...

  4. 详细解读:远程线程注入DLL到PC版微信

    一.远程线程注入的原理 1.其基础是在 Windows 系统中,每个 .exe 文件在双击打开时都会加载 kernel32.dll 这个系统模块,该模块中有一个 LoadLibrary() 函数,可以 ...

  5. 《windows核心编程系列》十九谈谈使用远程线程来注入DLL。

    windows内的各个进程有各自的地址空间.它们相互独立互不干扰保证了系统的安全性.但是windows也为调试器或是其他工具设计了一些函数,这些函数可以让一个进程对另一个进程进行操作.虽然他们是为调试 ...

  6. 系统权限远程线程注入到Explorer.exe

    目录 提升为系统权限,注入到explorer中 一丶简介 二丶注入效果图 提升为系统权限,注入到explorer中 一丶简介 我们上一面说了系统服务拥有系统权限.并且拥有system权限.还尝试启动了 ...

  7. Dll注入技术之远程线程注入

    DLL注入技术之远线程注入 DLL注入技术指的是将一个DLL文件强行加载到EXE文件中,并成为EXE文件中的一部分,这样做的目的在于方便我们通过这个DLL读写EXE文件内存数据,(例如 HOOK EX ...

  8. 远程线程注入DLL突破session 0 隔离

    远程线程注入DLL突破session 0 隔离 0x00 前言 补充上篇的远程线程注入,突破系统SESSION 0 隔离,向系统服务进程中注入DLL. 0x01 介绍 通过CreateRemoteTh ...

  9. 远程线程注入突破SESSION 0

    远程线程注入突破SESSION 0 SESSION 0 隔离 在Windows XP.Windows Server 2003,以及更老版本的Windows操作系统中,服务和应用程序使用相同的会话(Se ...

随机推荐

  1. Semaphore 类 的使用理解C#

    示例 下面的代码示例创建一个信号量,其最大计数为3,初始计数为零. 该示例启动五个线程,这会阻止等待信号量. 主线程使用 Release(Int32) 方法重载将信号量计数增加到其最大值,从而允许三个 ...

  2. 解析一个HTML字符串

    存在问题 来自用户输入,一个文件或一个网站的HTML字符串,你可能需要对它进行解析并取其内容,或校验其格式是否完整,或想修改它.怎么办?jsonu能够帮你轻松解决这些问题 解决方法 使用静态Jsoup ...

  3. Java中的比较器(排序)

    "顺序"在任何一个领域里都是非常重要的一个概念,程序也不例外.不同的执行顺序,能对你的执行结果产生直接影响. 既然涉及到顺序,那就要求排序.所以本文讨论的就是排序中使用到的比较器C ...

  4. OVN入门

    参考链接 如何借助 OVN 来提高 OVS 在云计算环境中的性能 OVN简介 Open vSwitch Documentation OVSDB介绍及在OpenDaylight中的调用 OpenDayl ...

  5. 3k+2形式的素数相关的两个命题的证明

  6. Kickstart部署多系统

    原文转自:https://www.cnblogs.com/itzgr/p/10029587.html作者:木二 目录 一 准备 1.1 完整架构:Kickstart+DHCP+HTTP+TFTP+PX ...

  7. Qt5之反射机制(内省)

    Qt的元对象系统除了提供信号/槽机制的特性之外,它还提供了以下特性: QObject::metaObject() 返回关联的元对象 QMetaObject::className() 在运行时状态下返回 ...

  8. 完蛋,公司被一条 update 语句干趴了!

    大家好,我是小林. 昨晚在群划水的时候,看到有位读者说了这么一件事. 在这里插入图片描述 大概就是,在线上执行一条 update 语句修改数据库数据的时候,where 条件没有带上索引,导致业务直接崩 ...

  9. Element 对话框简单使用

    官方文档介绍的是页内对话框,但没有基于组件的对话框,这里记录一下,原理就是父子传值是否显示 父页导入组件 <template> <div class="home" ...

  10. 前缀树及其Java实现

    前缀树 基础知识 Trie树.又称之为单词查找树或者键树,是一种树形结构.应用于统计和排序大量的字符串.常被搜索引擎系统用于文本词频统计.它的优点:能够最大限度的减少无谓的字符串比较,查询效率比哈希表 ...