目录
前言
远程线程注入是指一个进程在另一个进程中创建线程的技术。
函数介绍
OpenProcess函数
打开一个已存在的进程对象,并返回进程的句柄。
语法
HANDLE OpenProcess(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwProcessId
);
参数
[in] dwDesiredAccess
对进程对象的访问。参数可以是一个或多个进程访问权限。
[in] bInheritHandle
如果此值为TRUE,则此进程创建的进程将继承句柄。否则,进程不会继承这个句柄。
[in] dwProcessId
要打开的本地进程的标识符。
返回值
如果函数成功,则返回值是指定进程的打开句柄。
如果函数失败,则返回值为NULL。
VirtualAllocEx函数
在指定进程的虚拟地址空间保留、提交或更改内存状态。
语法
LPVOID VirtualAllocEx(
[in] HANDLE hProcess,
[in, optional] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flAllocationType,
[in] DWORD flProtect
);
参数
[in] hProcess
进程的句柄。
[in, optional] lpAddress
要分配的页面区域指定所需起始地址的指针。
[in] dwSize
要分配的内存区域的大小,以字节为单位。
[in] flAllocationType
内存分配的类型。
[in] flProtect
要分配的页面区域的内存保护。
返回值
如果函数成功,则返回值是页面分配区域的基地址。
如果函数失败,则返回值为零。
WriteProcessMemory函数
写入进程的内存区域。
语法
BOOL WriteProcessMemory(
[in] HANDLE hProcess,
[in] LPVOID lpBaseAddress,
[in] LPCVOID lpBuffer,
[in] SIZE_T nSize,
[out] SIZE_T *lpNumberOfBytesWritten
);
参数
[in] hProcess
要修改的进程内存的句柄。
[in] lpBaseAddress
指向指定进程中写入数据的基地址的指针。
[in] lpBuffer
要写入指定进程地址空间的数据的缓冲区的指针。
[in] nSize
要写入指定进程的字节数。
[out] lpNumberOfBytesWritten
一个指向接收传输到指定进程的字节数的变量的指针。该参数是可选的。
返回值
如果函数成功,则返回值非零。
如果函数失败,则返回值为0。
GetProcAddress函数
检索指定的DLL中的输出库函数地址。
语法
FARPROC GetProcAddress(
[in] HMODULE hModule,
[in] LPCSTR lpProcName
);
参数
[in] hModule
包含函数或变量的DLL模块的句柄。
[in] lpProcName
函数或变量名称,或函数的序数值。
返回值
如果函数成功,则返回值是导出函数或变量的地址。
如果函数失败,则返回值为NULL。
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
);
参数
[in] hProcess
要在其中创建线程的进程的句柄。
[in] lpThreadAttributes
指向SECURITY_ATTRIBUTES结构的指针,该结构为新线程指定安全描述符并确定子进程是否可以继承返回的句柄。
如果lpThreadAttributes为NULL,则线程获得一个默认的安全描述符并且句柄不能被继承。
[in] dwStackSize
堆栈的初始大小,以字节为单位。
[in] lpStartAddress
远程进程中线程的起始地址。
[in] lpParameter
指向要传递给线程函数的变量的指针。
[in] dwCreationFlags
控制线程创建的标志。
[out] lpThreadId
指向接收线程标识符的变量的指针。
返回值
如果函数成功,则返回值是新线程的句柄。
如果函数失败,则返回值为NULL。
示例
程序代码
RemoteThreadInject第一个参数为要被注入的PID,第二个参数为要注入的DLL文件。
// 远程线程注入.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <Windows.h>
//远程线程注入
bool RemoteThreadInject(int Pid, const char* Path)
{
//1.使用PID打开进程获取权限
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, Pid);
//2.申请内存,写入DLL路径
LPVOID pBuf = VirtualAllocEx(hProcess, NULL, strlen(Path) + 1, MEM_COMMIT, PAGE_READWRITE);
//3.写入内存
if (!WriteProcessMemory(hProcess, pBuf, Path, strlen(Path) + 1, NULL))
{
printf("写入内存失败!\n");
return false;
}
//4.创建远程线程,让对方调用LoadLibrary
HMODULE hModule = LoadLibrary("kernel32.dll");
LPTHREAD_START_ROUTINE pfnStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");
if (!pfnStartAddress)
{
printf("获取LoadLibraryA失败!\n");
return false;
}
else
{
printf("获取LoadLibraryA成功!\n");
}
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, NULL,
pfnStartAddress, pBuf, NULL, NULL);
if (NULL == hRemoteThread)
{
printf("目标进程中创建线程失败!\n");
CloseHandle(hProcess);
return FALSE;
}
else
{
printf("目标进程中创建线程成功!\n");
}
//5.等待线程结束返回,释放资源
WaitForSingleObject(hRemoteThread, 2000);
CloseHandle(hRemoteThread);
VirtualFreeEx(hProcess, pBuf, 0, MEM_FREE);
}
int main()
{
const char* dllpath = "D:\\testDll.dll";
RemoteThreadInject(15220, dllpath);
printf("执行结束");
return 0;
}
DLL代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// Sleep(1000000);
MessageBox(NULL, L"TIPS", L"TEST", NULL);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
测试
要被注入的进程“calc.exe”PID为1012;
注入DLL文件为“C:\Users\Administrator\Desktop\messagebox.dll”:
执行程序生成EXE:
使用ProcessExplorer查看进程:
原理学习
之所以被称为远程线程注入,是因为使用CreateRemoteThead函数在其他进程中创建一个线程。
kernel32.dll和user32.dll总是被映射到进程的内存首选地址;所有使用这两个DLL的进程中,这两个DLL的内存地址是相同的。我们在本进程中获取到的kernel32.dll中LoadLibrary函数的地址,和目标进程中是一样的。
在目标进程中使用CreateRemoteThead在目标进程中创建新的线程;使用LoadLibrary函数调用DLL。
对系统进程进行尝试注入,由于系统存在SESSION 0隔离,不能成功注入到系统进程。