要实现线程的远程注入必须使用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;

}

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

[java] view
plain
copy

  1. #pragma once
  2. #include <windows.h>
  3. #include <TlHelp32.h>
  4. #include "stdio.h"
  5. //线程参数结构体定义
  6. typedef struct _RemoteParam {
  7. char szMsg[12];    //MessageBox函数中显示的字符提示
  8. DWORD dwMessageBox;//MessageBox函数的入口地址
  9. } RemoteParam, * PRemoteParam;
  10. //定义MessageBox类型的函数指针
  11. typedef int (__stdcall * PFN_MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, DWORD);
  12. //线程函数定义
  13. DWORD __stdcall threadProc(LPVOID lParam)
  14. {
  15. //只要使用api必须拦截 !!!!!!!!
  16. RemoteParam* pRP = (RemoteParam*)lParam;
  17. PFN_MESSAGEBOX pfnMessageBox;
  18. pfnMessageBox = (PFN_MESSAGEBOX)pRP->dwMessageBox;
  19. //就是这句有错!!!!!!!!!
  20. pfnMessageBox(NULL, pRP->szMsg, pRP->szMsg, 0);
  21. return 0;
  22. }
  23. //提升进程访问权限
  24. bool enableDebugPriv()
  25. {
  26. HANDLE hToken;
  27. LUID sedebugnameValue;
  28. TOKEN_PRIVILEGES tkp;
  29. if (!OpenProcessToken(GetCurrentProcess(),
  30. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
  31. return false;
  32. }
  33. if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)) {
  34. CloseHandle(hToken);
  35. return false;
  36. }
  37. tkp.PrivilegeCount = 1;
  38. tkp.Privileges[0].Luid = sedebugnameValue;
  39. tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  40. if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) {
  41. CloseHandle(hToken);
  42. return false;
  43. }
  44. return true;
  45. }
  46. //根据进程名称得到进程ID,如果有多个运行实例的话,返回第一个枚举到的进程的ID
  47. DWORD processNameToId(LPCTSTR lpszProcessName)
  48. {
  49. HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  50. PROCESSENTRY32 pe;
  51. pe.dwSize = sizeof(PROCESSENTRY32);
  52. if (!Process32First(hSnapshot, &pe)) {
  53. MessageBox(NULL,
  54. "The frist entry of the process list has not been copyied to the buffer",
  55. "Notice", MB_ICONINFORMATION | MB_OK);
  56. return 0;
  57. }
  58. while (Process32Next(hSnapshot, &pe)) {
  59. if (!strcmp(lpszProcessName, pe.szExeFile)) {
  60. return pe.th32ProcessID;
  61. }
  62. }
  63. return 0;
  64. }
  65. int main(int argc, char* argv[])
  66. {
  67. //定义线程体的大小
  68. const DWORD dwThreadSize = 40Array6;
  69. DWORD dwWriteBytes;
  70. //提升进程访问权限
  71. enableDebugPriv();
  72. //等待输入进程名称,注意大小写匹配
  73. char szExeName[MAX_PATH] = { 0 };
  74. //    cout<< "Please input the name of target process !" <<endl;
  75. //
  76. //    cin >> szExeName;
  77. // cout<<szExeName<<endl;
  78. //strcpy(szExeName,"notepad.exe");
  79. scanf("%s",szExeName);
  80. DWORD dwProcessId = processNameToId(szExeName);
  81. if (dwProcessId == 0) {
  82. MessageBox(NULL, "The target process have not been found !",
  83. "Notice", MB_ICONINFORMATION | MB_OK);
  84. return -1;
  85. }
  86. //根据进程ID得到进程句柄
  87. HANDLE hTargetProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
  88. if (!hTargetProcess) {
  89. MessageBox(NULL, "Open target process failed !",
  90. "Notice", MB_ICONINFORMATION | MB_OK);
  91. return 0;
  92. }
  93. //在宿主进程中为线程体开辟一块存储区域
  94. //在这里需要注意MEM_COMMIT | MEM_RESERVE内存非配类型以及PAGE_EXECUTE_READWRITE内存保护类型
  95. //其具体含义请参考MSDN中关于VirtualAllocEx函数的说明。
  96. void* pRemoteThread = VirtualAllocEx(hTargetProcess, 0,
  97. dwThreadSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  98. if (!pRemoteThread) {
  99. MessageBox(NULL, "Alloc memory in target process failed !",
  100. "notice", MB_ICONINFORMATION | MB_OK);
  101. return 0;
  102. }
  103. //将线程体拷贝到宿主进程中
  104. if (!WriteProcessMemory(hTargetProcess,
  105. pRemoteThread, &threadProc, dwThreadSize, 0)) {
  106. MessageBox(NULL, "Write data to target process failed !",
  107. "Notice", MB_ICONINFORMATION | MB_OK);
  108. return 0;
  109. }
  110. //定义线程参数结构体变量
  111. RemoteParam remoteData;
  112. ZeroMemory(&remoteData, sizeof(RemoteParam));
  113. //填充结构体变量中的成员
  114. HINSTANCE hUser32 = LoadLibrary("User32.dll");
  115. remoteData.dwMessageBox = (DWORD)GetProcAddress(hUser32, "MessageBoxA");
  116. strcat(remoteData.szMsg, "Hello\0");
  117. //为线程参数在宿主进程中开辟存储区域
  118. RemoteParam* pRemoteParam = (RemoteParam*)VirtualAllocEx(
  119. hTargetProcess , 0, sizeof(RemoteParam), MEM_COMMIT, PAGE_READWRITE);
  120. if (!pRemoteParam) {
  121. MessageBox(NULL, "Alloc memory failed !",
  122. "Notice", MB_ICONINFORMATION | MB_OK);
  123. return 0;
  124. }
  125. //将线程参数拷贝到宿主进程地址空间中
  126. if (!WriteProcessMemory(hTargetProcess ,
  127. pRemoteParam, &remoteData, sizeof(remoteData), 0)) {
  128. MessageBox(NULL, "Write data to target process failed !",
  129. "Notice", MB_ICONINFORMATION | MB_OK);
  130. return 0;
  131. }
  132. //在宿主进程中创建线程
  133. HANDLE hRemoteThread = CreateRemoteThread(
  134. hTargetProcess, NULL, 0, (DWORD (__stdcall *)(void *))pRemoteThread,
  135. pRemoteParam, 0, &dwWriteBytes);
  136. if (!hRemoteThread) {
  137. MessageBox(NULL, "Create remote thread failed !", "Notice",  MB_ICONINFORMATION | MB_OK);
  138. return 0;
  139. }
  140. CloseHandle(hRemoteThread);
  141. FreeLibrary(hUser32);
  142. return 0;
  143. }

CreateRemoteThread简单应用的更多相关文章

  1. EasyHook远注简单监控示例 z

    http://www.csdn 123.com/html/itweb/20130827/83559_83558_83544.htm 免费开源库EasyHook(inline hook),下面是下载地址 ...

  2. 远程线程注入方法CreateRemoteThread

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

  3. VC API常用函数简单例子大全(1-89)

    第一个:FindWindow根据窗口类名或窗口标题名来获得窗口的句柄,该函数返回窗口的句柄 函数的定义:HWND WINAPI FindWindow(LPCSTR lpClassName ,LPCST ...

  4. 使用CreateRemoteThread把代码远程注入指定exe执行

    由于本人也是新手,如果有朋友不懂windows api相关知识,我相信查阅书籍或者百度会比我说有帮助的多,下面就我所做简单复述一下过程,欢迎指正缺点. 效果图示如下: 做的这个例子首先是创建了一个MF ...

  5. CreateRemoteThread创建远程线程

    要实现线程的远程注入必须使用Windows提供的CreateRemoteThread函数来创建一个远程线程 该函数的原型如下: HANDLE CreateRemoteThread( HANDLE hP ...

  6. 【造轮子】打造一个简单的万能Excel读写工具

    大家工作或者平时是不是经常遇到要读写一些简单格式的Excel? shit!~很蛋疼,因为之前吹牛,就搞了个这东西,还算是挺实用,和大家分享下. 厌烦了每次搞简单类型的Excel读写?不怕~来,喜欢流式 ...

  7. Fabio 安装和简单使用

    Fabio(Go 语言):https://github.com/eBay/fabio Fabio 是一个快速.现代.zero-conf 负载均衡 HTTP(S) 路由器,用于部署 Consul 管理的 ...

  8. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  9. 哪种缓存效果高?开源一个简单的缓存组件j2cache

    背景 现在的web系统已经越来越多的应用缓存技术,而且缓存技术确实是能实足的增强系统性能的.我在项目中也开始接触一些缓存的需求. 开始简单的就用jvm(java托管内存)来做缓存,这样对于单个应用服务 ...

随机推荐

  1. SQL索引操作

    1. 创建索引 create index 索引名 on 表名(列名); 2. 删除索引 drop index 索引名; 3. 创建组合索引 create index 索引名 on 表名(列名1,,列名 ...

  2. 【学术篇】SPOJ FTOUR2 点分治

    淀粉质入门第一道 (现在个人认为spoj比bzoj要好_(:з」∠)_ 关于点分治的话推荐去看一看漆子超的论文>>>这里这里<<< 之前一直试图入点分治坑, 但是因 ...

  3. Mysql的Event

    Mysql的Event Event简介 Event是mysql中的一个事件,和触发器类似,触发器是在某条sql语句执行后可能会触发,而Event是每隔一段时间或某个特定的时间点执行,可以精确到秒. 准 ...

  4. java创建一个空白zip

    String zipath = localpath+zipname+".zip"; public static void createNewzip(String zipath) t ...

  5. forEarch 和 for in

    forEarch 遍历数组,遍历的过程中不能被终止,必须每一个值遍历一遍后才能停下来,for  in遍历对象中的属性 代码: <!DOCTYPE html> <html lang=& ...

  6. Redis缓存数据库常见操作

    Jedis的最为常见的操作.主要包括常用的列表(list).集合(set).有序集合(sorted set).哈希表(hash)等数据结构,以及其他特性支持. 参考资料:http://hello-ni ...

  7. HAProxy服务器 、Keepalived热备 、Keepalived+LVS

    配置HAProxy负载平衡集群 1.1 问题 准备三台Linux服务器,两台做Web服务器,一台安装HAProxy,实现如下功能: 客户端访问HAProxy,HAProxy分发请求到后端Real Se ...

  8. NX二次开发-NXOpen::WCS Class Reference

    NX11+VS2013 #include <NXOpen/Part.hxx> #include <NXOpen/PartCollection.hxx> #include < ...

  9. NX二次开发-C++ DeleteFile删除文件实例代码

    NX9+VS2012 #include<Windows.h> DeleteFile("D:\\1\\test123.prt"); Caesar卢尚宇 2019年7月29 ...

  10. flutter 图片为空报错

    imgpath != null ? Image.network(imgpath) : Container() 如果不判断imgpath 为空 network 内的url 为空就会报错