1.C语言创建线程:
头文件process.h
uintptr_t _beginthread(
void( *start_address )( void * ), //回调函数地址
unsigned stack_size, //栈大小
void *arglist //用户自定义参数
);
uintptr_t _beginthreadex(
void *security, //安全属性
unsigned stack_size,
unsigned ( *start_address )( void * ),
void *arglist,
unsigned initflag, //线程初始状态,0为就绪,CREATE_SUSPENDED表示暂停
unsigned *thrdaddr //线程id指针,可为NULL
);
使用:
#include
#include
#include
int g_val = 0;
HANDLE hd[4] = {};
using std::cout;
using std::endl;
unsigned callback(void* param)
{
for (int i = 0; i < 1000000; i++)
{
g_val++;
}
cout << "值:" << g_val <<"线程id:"<
运行结果:
2.C++11线程
C++11将多线程引入标准库中,不需要再使用CreateThread函数创建线程
参考thread::thread - C++ Reference
thread构造函数如下:
template
explicit thread (Fn&& fn, Args&&... args);
//Fn为函数指针,Args是变长参数
等待线程结束:
std::thread::join() 类型void join()
注意:不能直接调system("pause")因为thread对象是一个与线程相关的对象,存储线程相关信息,所以析构时会认为绑定的线程没有结束而抛出异常
将thread对象与创建的线程解绑:
std::thread::detach() 类型void detach()
thread头文件的this_thread命名空间提供了其他一些访问和操作线程的相关函数,参考this_thread - C++ Reference
获取线程id:
thread::id get_id() noexcept;
sleep
template
void sleep_for (const chrono::duration& rel_time);
template
void sleep_until (const chrono::time_point& abs_time);
chrono是C++的事件库,duration是与时间有关的对象,参考
使用示例:
this_thread::sleep_for(std::chrono::seconds(5)); //线程休眠5s
其他时间度量如下:
#include
#include
#include
#include
using namespace std;
class _mythread
{
public:
void callback(int param)
{
for (int i = 0; i < 100; i++)
{
g_val++;
}
this_thread::sleep_for(chrono::seconds(1));
cout << "值:" << g_val << "线程id:" << this_thread::get_id() <<"自定义参数为:"<
使用std::thread的好处是可以跨平台,这样代码在Linux上也跑的起来
3.远程线程注入
远程线程注入就是在其他进程中创建我们的线程,我们的线程会把我们的dll载入到注入进程中
创建远程线程api:
HANDLE WINAPI 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
);
与CreateThread函数相比只是多了第一个参数进程句柄,比较如下两个函数
DWORD WINAPI ThreadProc(
__in LPVOID lpParameter
);
HMODULE WINAPI LoadLibrary(
__in LPCTSTR lpFileName
);
可以发现两个函数形式基本相同,所以我们可以将LoadLibrary作为线程回调函数,但是我们不能直接将LoadLibrary作为参数传入CreateThread(参加PE文件结构),不过由于系统dll(如kernel32.dll)在所有进程中地址是相同的(重启后可能变化),我们可以使用如下函数获取LoadLibrary函数地址
FARPROC WINAPI GetProcAddress(
//获取dll模块中函数地址
__in HMODULE hModule, //dll模块句柄
__in LPCSTR lpProcName //(导出)函数名
);
HMODULE WINAPI GetModuleHandle(
//获取模块句柄
__in_opt LPCTSTR lpModuleName //模块名
);
注意:LoadLibrary实际上是LoadLibraryA和LoadLibraryW,分别对于ANSI和Unicode编码,获取函数地址时需要根据dll文件名是哪种编码形式保存的来选择获取哪个LoadLibrary函数
还有一个问题是指向dll文件路径的指针是在本地进程而非远程进程中,我们把它作为参数传给远程线程,远程线程再传给loadlibrary,由于每个进程都有自己独立的虚拟内存空间,所以loadlibrary在从远程线程传入的参数的地址中找不到dll文件路径,并且可能会导致远程进程因违规访问而崩溃,所以我们需要把dll路径字符串存入远程进程的地址空间中,windows提供了如下函数来在另一个进程开辟内存空间:
//开辟内存
LPVOID WINAPI VirtualAllocEx(
__in HANDLE hProcess, //进程句柄
__in_opt LPVOID lpAddress, //指向要开辟内存的起始地址的指针,填NULL由api自己找
__in SIZE_T dwSize, //大小,与分页对齐
__in DWORD flAllocationType, //分配类型,一般填MEM_COMMIT,指明开辟的虚拟内存要映射进物理内存
__in DWORD flProtect //内存分页权限,填PAGE_READWRITE表示可读可写
);
//释放内存
BOOL WINAPI VirtualFreeEx(
__in HANDLE hProcess,
__in LPVOID lpAddress,
__in SIZE_T dwSize,
__in DWORD dwFreeType
);
//读取内存
BOOL WINAPI ReadProcessMemory(
__in HANDLE hProcess,
__in LPCVOID lpBaseAddress, //远程进程中的地址
__out LPVOID lpBuffer, //本地进程中的缓冲区地址
__in SIZE_T nSize, //要传输的字节数
__out SIZE_T* lpNumberOfBytesRead //实际传输的字节数
);
//写入内存
BOOL WINAPI WriteProcessMemory(
__in HANDLE hProcess,
__in LPVOID lpBaseAddress,
__in LPCVOID lpBuffer,
__in SIZE_T nSize,
__out SIZE_T* lpNumberOfBytesWritten
);
由上可以看出我们使用远程线程注入dll分为以下几步:
1.在远程进程的地址空间中开辟一段内存空间
2.写入dll的路径到开辟的内存中
3.获取LoadLibraryW或LoadLibraryW函数地址(通过Kernel32.dll)
4.创建远程线程,传入参数
5.释放内存
6.使用GetProcAddress来获取FreeLibrary函数在Kernel32.dll的地址
7.创建远程线程调用FreeLibrary释放我们的dll
示例:
cpp:
#include
#include
using namespace std;
int main()
{
HWND hw = FindWindow(NULL, "AntConc"); //获取窗体句柄
DWORD wid = 0;
GetWindowThreadProcessId(hw, &wid); //获取窗体线程id
HANDLE hd=OpenProcess(PROCESS_ALL_ACCESS, FALSE, wid); //获取进程句柄
//在远程进程的地址空间中开辟一段内存空间
LPVOID pPath=VirtualAllocEx(hd, NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//写入dll的路径到开辟的内存中
char buffer[] = { "C:\\Users\\MSI-NB\\source\\repos\\Dlltest\\x64\\Debug\\Dlltest.dll" };
WriteProcessMemory(hd, pPath, buffer, sizeof(buffer), NULL);
//获取LoadLibraryW或LoadLibraryW函数地址(通过Kernel32.dll)
LPTHREAD_START_ROUTINE pload = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32.dll"), "LoadLibraryA"); //这里用的是多字节字符集
//创建远程线程,传入参数
HANDLE thread=CreateRemoteThread(hd, NULL, 0, pload, pPath, 0, NULL);
WaitForSingleObject(thread, INFINITE);
//释放内存
VirtualFreeEx(hd, pPath, 0x1000, MEM_DECOMMIT);
//使用GetProcAddress来获取FreeLibrary函数在Kernel32.dll的地址
LPTHREAD_START_ROUTINE pfree = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32.dll"), "FreeLibrary");
//创建远程线程调用FreeLibrary释放我们的dll
thread = CreateRemoteThread(hd, NULL, 0, pfree, NULL, 0, NULL);
WaitForSingleObject(thread, INFINITE);
system("pause");
}
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:
{
HWND hw = FindWindow(NULL, "AntConc");
MessageBox(hw, "hello", "hi", MB_OK);
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
示例中将dll注入到antconc中,注入成功dllmain收到DLL_PROCESS_ATTACH后将会执行
运行结果: