要实现线程的远程注入必须使用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. vue axios----基于 Promise 的 HTTP 请求

    vue axiosvue2.0之axios接口請求管理功能特性axios API開始使用get請求post请求多个请求并发拦截器移除一个拦截器:自定义的 axios 实例添加拦截器:vue2.0之ax ...

  3. 沉默的QQ

    QQ很久没有叮咚了,一是没人发消息给我,二是我关了QQ的声音.现在除了几个还在接收消息的群外,其他的群都屏蔽了,有事才上去看看.我从来就没写过QQ空间,那些以前经常写空间的朋友也多数停止了喧闹.每次登 ...

  4. 主席树/线段树模拟归并排序+二分答案(好题)——hdu多校第4场08

    用主席树写起来跑的快一点,而且也很傻比,二分答案,即二分那个半径就行 主席树求的是区间<=k的个数 #include<bits/stdc++.h> using namespace s ...

  5. NX二次开发-UFUN参数选择对话框UF_UI_select_parameters

    #include <uf.h> #include <uf_ui.h> #include <uf_modl.h> UF_initialize(); //参数选择对话框 ...

  6. NX二次开发-UFUN导入表达式UF_MODL_import_exp

    最近在做表达式创建,发现UFUN的创建表达式函数UF_MODL_create_exp,UF_MODL_create_exp_tag没有办法创建字符串类型的表达式,例如AA="BB" ...

  7. 关于double的输入输出

    double定义的变量输入的时候一定要%lf输入,要是%f输入的话,得到的结果会是0 float输入的时候是%f 但是在输出的时候%lf和%f都可以输出 建议使用double类型时,用%lf输入,%f ...

  8. day 89 DjangoRestFramework学习三之认证组件、权限组件、频率组件、url注册器、响应器、分页组件

    DjangoRestFramework学习三之认证组件.权限组件.频率组件.url注册器.响应器.分页组件   本节目录 一 认证组件 二 权限组件 三 频率组件 四 URL注册器 五 响应器 六 分 ...

  9. CSS3教程:Responsive框架常见的Media Queries片段

    CSS3 Media Queries片段在这里主要分成三类:移动端.PC端以及一些常见的响应式框架的Media Queries片段.移动端Media Queries片段iPhone5@media sc ...

  10. DRF的三大认证组件

    目录 DRF的三大认证组件 认证组件 工作原理 实现 权限组件 工作原理 实现 频率组件 工作原理 实现 三种组件的配置 DRF的三大认证组件 认证组件 工作原理 首先,认证组件是基于BaseAuth ...