zoukankan      html  css  js  c++  java
  • C/C++ 实现常用的线程注入

    各种API远程线程注入的方法,分别是 远程线程注入,普通消息钩子注入,全局消息钩子注入,APC应用层异步注入,ZwCreateThreadEx强力注入,纯汇编实现的线程注入等。

    简单编写DLL文件:

    #include <Windows.h>
    
    extern "C" __declspec(dllexport) void MsgBox(LPCWSTR szMsg, LPCWSTR Title)
    {
    	MessageBox(NULL, szMsg, Title, MB_OK);
    }
    
    BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
    {
    	switch (ul_reason_for_call)
    	{
    	case DLL_PROCESS_ATTACH:
    		// 进程被加载后执行
    		break;
    	case DLL_THREAD_ATTACH:
    		// 线程被创建后加载
    		break;
    	case DLL_THREAD_DETACH:
    		// 正常退出执行的代码
    		break;
    	case DLL_PROCESS_DETACH:
    		// 进程卸载本Dll后执行的代码
    		break;
    	}
    	return TRUE;
    }
    
    #include <Windows.h>
    #include <iostream>
    
    typedef VOID(*PFUNMSG)(LPCWSTR szMsg, LPCWSTR Title);
    
    int main(int argc, char *argv[])
    {
    
    	HMODULE hModule = LoadLibrary("./hook.dll");
    
    	if (hModule != NULL)
    	{
    		PFUNMSG pMsgBox = (PFUNMSG)GetProcAddress(hModule, "MsgBox");
    		pMsgBox(L"hello lyshark", L"msgbox");
    	}
    
    	system("pause");
    	return 0;
    }
    

    x86 实现远程线程注入: 注入原理是利用了Windows系统中提供的CreateRemoteThread()这个API函数,该函数第四个参数是准备运行的线程,我们将LoadLibrary()函数填入其中,这样就可以执行远程进程中的LoadLibrary()函数,进而将我们自己准备的DLL加载到远程进程空间中执行,DLL在被装载后则会自动执行初始化部分,X86注入代码如下.

    #include <windows.h>
    #include <stdio.h>
    
    // 使用 CreateRemoteThread 实现远线程注入
    BOOL CreateRemoteThreadInjectDll(DWORD Pid, char *DllName)
    {
    	HANDLE hProcess = NULL;
    	SIZE_T dwSize = 0;
    	LPVOID pDllAddr = NULL;
    	FARPROC pFuncProcAddr = NULL;
    
    	// 打开注入进程,获取进程句柄
    	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
    	if (NULL == hProcess)
    	{
    		return FALSE;
    	}
    	// 计算欲注入 DLL 文件完整路径的长度
    	dwSize = sizeof(char)+lstrlen(DllName);
    
    	//  在目标进程申请一块长度为 nDllLen 大小的内存空间
    	pDllAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
    	if (NULL == pDllAddr)
    	{
    		return FALSE;
    	}
    	//  将欲注入 DLL 文件的完整路径写入在目标进程中申请的空间内
    	if (FALSE == WriteProcessMemory(hProcess, pDllAddr, DllName, dwSize, NULL))
    	{
    		return FALSE;
    	}
    	// 获得 LoadLibraryA()函数的地址
    	pFuncProcAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
    	if (NULL == pFuncProcAddr)
    	{
    		return FALSE;
    	}
    	// 使用 CreateRemoteThread 创建远线程, 实现 DLL 注入
    	HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, NULL);
    	if (NULL == hRemoteThread)
    	{
    		return FALSE;
    	}
    	// 关闭句柄
    	CloseHandle(hProcess);
    	return TRUE;
    }
    
    int main(int argc, char *argv[])
    {
    	CreateRemoteThreadInjectDll(4668, "./x86.dll");
    	system("pause");
    	return 0;
    }
    

    x64 实现远程线程注入: 如果想要注入X64程序,则需要在编译时指定为64位编译模式,并且使用LoadLibraryW()来加载动态链接库,我们只需要在上面代码的基础上稍加改进就可以实现64位进程的注入了.

    #include <windows.h>
    #include <stdio.h>
    
    // 使用 CreateRemoteThread 实现远线程注入
    BOOL CreateRemoteThreadInjectDll(DWORD Pid, PCWSTR DllName)
    {
    	BOOL ret = FALSE;
    	HANDLE hProcess, hThread = NULL;
    	FARPROC pfnThreadRtn = NULL;
    	PWSTR pwszPara = NULL;
    
    	// 打开注入进程,获取进程句柄
    	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
    	if (NULL == hProcess)
    	{
    		return FALSE;
    	}
    
    	// 计算欲注入 DLL 文件完整路径的长度
    	size_t iProxyFileLen = wcslen(DllName) * sizeof(WCHAR);
    
    	//  在目标进程申请一块长度为 nDllLen 大小的内存空间
    	pwszPara = (PWSTR)VirtualAllocEx(hProcess, NULL, iProxyFileLen, MEM_COMMIT, PAGE_READWRITE);
    	if (NULL == pwszPara)
    	{
    		return FALSE;
    	}
    
    	//  将欲注入 DLL 文件的完整路径写入在目标进程中申请的空间内
    	if (FALSE == WriteProcessMemory(hProcess, pwszPara, (PVOID)DllName, iProxyFileLen, NULL))
    	{
    		return FALSE;
    	}
    
    	// 获得 LoadLibraryW()函数的地址
    	pfnThreadRtn = GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
    	if (NULL == pfnThreadRtn)
    	{
    		return FALSE;
    	}
    
    	// 使用 CreateRemoteThread 创建远线程, 实现 DLL 注入
    	hThread = CreateRemoteThread(hProcess, NULL, 1024, (LPTHREAD_START_ROUTINE)pfnThreadRtn, pwszPara, 0, NULL);
    	WaitForSingleObject(hThread, INFINITE);
    	if (NULL != hThread)
    	{
    		CloseHandle(hThread);
    		CloseHandle(hProcess);
    		VirtualFreeEx(hProcess, pwszPara, 0, MEM_RELEASE);
    		return TRUE;
    	}
    	return FALSE;
    }
    
    int main(int argc, char *argv[])
    {
    	CreateRemoteThreadInjectDll(8224, L"./x64.dll");
    	system("pause");
    	return 0;
    }
    

    实现普通消息钩子注入: Windows提供的钩子类型非常多,其中一种类型的钩子非常实用,那就是WH_GETME SSAGE钩子,它可以很方便地将DLL文件注入到所有的基于消息机制的目标程序中,代码非常简单,这里直接给出DLL文件的代码,具体如下:

    #include <windows.h>
    
    extern "C" __declspec(dllexport) VOID SetHookOn();
    extern "C" __declspec(dllexport) VOID SetHookOff();
    
    HHOOK g_HHook = NULL;
    HINSTANCE g_hInst = NULL;
    
    VOID DoSomeThing()
    {
    	MessageBoxA(0, "hello lyshark", 0, 0);
    }
    
    BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
    {
    	switch (fdwReason)
    	{
    		case DLL_PROCESS_ATTACH:
    		{
    			g_hInst = hinstDLL;
    			DoSomeThing();
    			break;
    		}
    	}
    	return TRUE;
    }
    
    LRESULT CALLBACK GetMsgProc(int code,WPARAM wParam,LPARAM lParam)
    {
    	return CallNextHookEx(g_HHook, code, wParam, lParam);
    }
    
    VOID SetHookOn()
    {
    	g_HHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInst, 0);
    }
    
    VOID SetHookOff()
    {
    	UnhookWindowsHookEx(g_HHook);
    }
    

    实现全局消息钩子注入: 该注入的原理是利用系统中的SetWindowHookEx()这个API函数,该函数可以拦截目标进程的消息到指定DLL中的导出函数上,利用这个特性,我们可以将DLL注入到全局进程中,但是在使用SetWindowsHookEx()之前首先需要将HOOK的DLL加载到本身的进程中,以此得到DLL的模块句柄,再使用GetProcAddress()得到DLL中公开的函数地址,最后遍历出待注入进程的线程ID,这样SetWindowHookEx()就可以利用这些参数进行HOOK了.

    我们先来编写DLL文件,创建Dll工程hook.cpp然后将SetHook()函数导出,由于该注入方式是全局注入,所以如果我们想要注入到指定进程中,则需要在DllMain()也就是动态链接库开头位置进行判断,如果是我们需要Hook的进程,则加载Dll到指定进程中,如果不是则不执行任何操作,这样一来即可实现指定进程注入.

    #include <windows.h>
    HHOOK Global_Hook;
    
    // 设置全局消息回调函数
    LRESULT CALLBACK MyProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
    	return CallNextHookEx(Global_Hook, nCode, wParam, lParam);
    }
    // 安装全局钩子,此处的hook.dll可以是外部其他的dll
    extern "C" __declspec(dllexport) void SetHook()
    {
    	Global_Hook = SetWindowsHookEx(WH_CBT, MyProc, GetModuleHandle(TEXT("hook.dll")), 0);
    }
    // 卸载全局钩子
    extern "C" __declspec(dllexport) void UnHook()
    {
    	if(Global_Hook)
    		UnhookWindowsHookEx(Global_Hook);
    }
    
    bool APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
    {
    	HWND hwnd = FindWindowW(L"valve001",NULL);
    	DWORD pid;
    	GetWindowThreadProcessId(hwnd, &pid);
    	if (GetCurrentProcessId() == pid)
    	{
    		MessageBox(hwnd, TEXT("hello lyshark"), 0, 0);
    	}
    	return true;
    }
    

    调用代码:注意必须将上方编译好的hook.dll与下方工程放到同一个目录下,通过LoadLibrary()函数获取到模块句柄,然后通过GetProcAddress()获取到导出函数地址,并通过函数指针调用,由于全局注入依赖于父进程,所以下面的代码必须一直运行.

    #include <windows.h>
    
    int main(int argc, char *argv[])
    {
    	HMODULE hMod = LoadLibrary(TEXT("hook.dll"));
    
    	typedef void(*pSetHook)(void);
    	pSetHook SetHook = (pSetHook)GetProcAddress(hMod, "SetHook");
    	SetHook();
    	while (1)
    	{
    		Sleep(1000);
    	}
    	return 0;
    }
    

    APC应用层异步注入: APC 是异步过程调用,在Windows下每个线程在可被唤醒时在其APC链中的函数将有机会执行被执行,每一个线程都具有一个APC链,那么只要在可以在APC链中添加一个APC,就可以完成我们所需要的DLL注入的功能.

    1.将需要加载的DLL的完整路径写入目标进程空间.
    2.获得LoadLibraryA()函数的地址,当然也可以是LoadLibraryW()函数的地址.
    3.枚举目标进程中的所有线程,为每个线程添加一个APC函数,这样增加了注入成功的机会.

    该注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,通过APC注入的流程步骤大致如下

    1.当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断.
    2.当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数.
    3.利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的,不论如何目标程序必须有执行SleepEx()或者WaitForSingleObjectEx()否则DLL不会加载.

    #include <windows.h>
    #include <tlhelp32.h>
    #include <stdio.h>
    
    // APC注入
    BOOL ApcInjectDll(DWORD dwPid, char * szDllName)
    {
    	// 计算欲注入 DLL 文件完整路径的长度
    	int nDllLen = lstrlen(szDllName) + sizeof(char);
    
    	// 打开目标进程
    	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwPid);
    	if (hProcess == NULL)
    	{
    		return FALSE;
    	}
    	// 在目标进程申请一块长度为 nDllLen 大小的内存空间
    	PVOID pDllAddr = VirtualAllocEx(hProcess,NULL, nDllLen,MEM_COMMIT,PAGE_READWRITE);
    	if (pDllAddr == NULL)
    	{
    		CloseHandle(hProcess);
    		return FALSE;
    	}
    	DWORD dwWriteNum = 0;
    	// 将欲注入 DLL 文件的完整路径写入在目标进程中申请的空间内
    	WriteProcessMemory(hProcess, pDllAddr, szDllName,nDllLen, &dwWriteNum);
    	CloseHandle(hProcess);
    
    	THREADENTRY32 te = { 0 };
    	te.dwSize = sizeof(THREADENTRY32);
    	//得到线程快照
    	HANDLE handleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    	if (INVALID_HANDLE_VALUE == handleSnap)
    	{
    		CloseHandle(hProcess);
    		return FALSE;
    	}
    	// 获得 LoadLibraryA()函数的地址
    	FARPROC pFunAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
    	DWORD dwRet = 0;
    	//得到第一个线程
    	if (Thread32First(handleSnap, &te))
    	{
    		do
    		{
    			//进行进程 ID 对比
    			if (te.th32OwnerProcessID == dwPid)
    			{
    				//得到线程句柄
    				HANDLE hThread = OpenThread(THREAD_ALL_ACCESS,FALSE,te.th32ThreadID);
    				if (hThread)
    				{
    					//向线程插入 APC
    					dwRet = QueueUserAPC((PAPCFUNC)pFunAddr,hThread,(ULONG_PTR)pDllAddr);
    					//关闭句柄
    					CloseHandle(hThread);
    				}
    			}
    			//循环下一个线程
    		} while (Thread32Next(handleSnap, &te));
    	}
    	CloseHandle(handleSnap);
    	return TRUE;
    }
    
    int main(int argc, char *argv[])
    {
    	ApcInjectDll(9608, "c:/x86.dll");
    	system("pause");
    	return 0;
    }
    

    ZwCreateThreadEx强力注入: 在前面的注入方式中,我们使用了CreateRemoteThread()这个函数来完成线程注入,此方式可以注入普通的进程,但却无法注入到系统进程中,因为系统进程是处在SESSION0高权限级别的会话层.

    由于CreateRemoteThread()底层会调用ZwCreateThreadEx()这个未公开的内核函数,所以我们必须手动调用ZwCreateThread()这一内核函数,将第七个参数设置为0即可,ZwCreateThreadEx函数在ntdll.dll中并未声明,所以必须手动使用GetProcAddress函数将其地址导出.

    #include <windows.h>
    #include <stdio.h>
    
    // 使用 ZwCreateThreadEx 实现远线程注入
    BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char * pDllName)
    {
    	HANDLE hProcess = NULL;
    	SIZE_T dwSize = 0;
    	LPVOID pDllAddr = NULL;
    	FARPROC pFuncProcAddr = NULL;
    	HANDLE hRemoteThread = NULL;
    	DWORD dwStatus = 0;
    
    	// 打开注入进程,获取进程句柄
    	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    	if (NULL == hProcess)
    	{
    		return FALSE;
    	}
    	// 在注入进程中申请内存
    	dwSize = sizeof(char)+lstrlen(pDllName);
    	pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
    	if (NULL == pDllAddr)
    	{
    		return FALSE;
    	}
    	// 向申请的内存中写入数据
    	if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pDllName, dwSize, NULL))
    	{
    		return FALSE;
    	}
    	// 加载 ntdll.dll
    	HMODULE hNtdllDll = ::LoadLibrary("ntdll.dll");
    	if (NULL == hNtdllDll)
    	{
    		return FALSE;
    	}
    	// 获取LoadLibraryA函数地址
    	pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
    	if (NULL == pFuncProcAddr)
    	{
    		return FALSE;
    	}
    	// 获取ZwCreateThread函数地址
    #ifdef _WIN64
    	typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
    		PHANDLE ThreadHandle,
    		ACCESS_MASK DesiredAccess,
    		LPVOID ObjectAttributes,
    		HANDLE ProcessHandle,
    		LPTHREAD_START_ROUTINE lpStartAddress,
    		LPVOID lpParameter,
    		ULONG CreateThreadFlags,
    		SIZE_T ZeroBits,
    		SIZE_T StackSize,
    		SIZE_T MaximumStackSize,
    		LPVOID pUnkown);
    #else
    	typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
    		PHANDLE ThreadHandle,
    		ACCESS_MASK DesiredAccess,
    		LPVOID ObjectAttributes,
    		HANDLE ProcessHandle,
    		LPTHREAD_START_ROUTINE lpStartAddress,
    		LPVOID lpParameter,
    		BOOL CreateSuspended,
    		DWORD dwStackSize,
    		DWORD dw1,
    		DWORD dw2,
    		LPVOID pUnkown);
    #endif
    	typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
    	if (NULL == ZwCreateThreadEx)
    	{
    		return FALSE;
    	}
    	// 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入
    	dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, 
    		(LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
    	if (NULL == hRemoteThread)
    	{
    		return FALSE;
    	}
    	// 关闭句柄
    	::CloseHandle(hProcess);
    	::FreeLibrary(hNtdllDll);
    	return TRUE;
    }
    
    int main(int argc, char *argv[])
    {
    	BOOL bRet = ZwCreateThreadExInjectDll(2940, "hook.dll");
    
    	system("pause");
    	return 0;
    }
    

    强制卸载进程中的DLL:

    #include <Windows.h>
    #include <stdio.h>
    #include <TlHelp32.h>
    
    BOOL UnLoad_Module(DWORD dwPID, LPCTSTR szDllName)
    {
    	BOOL bMore = FALSE, bFound = FALSE;
    	HANDLE hSnapshot, hProcess, hThread;
    	HMODULE hModule = NULL;
    	MODULEENTRY32 me = { sizeof(me) };
    	LPTHREAD_START_ROUTINE pThreadProc;
    
    	hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
    	bMore = Module32First(hSnapshot, &me);
    	for (; bMore; bMore = Module32Next(hSnapshot, &me))
    	{
    		if (!_tcsicmp((LPCTSTR)me.szModule, szDllName) || !_tcsicmp((LPCTSTR)me.szExePath, szDllName))
    		{
    			bFound = TRUE;
    			break;
    		}
    	}
    	if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
    		return FALSE;
    
    	hModule = GetModuleHandle(L"kernel32.dll");
    	pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");
    	hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);
    	WaitForSingleObject(hThread, INFINITE);
    	if (hThread != 0)
    	{
    		CloseHandle(hThread);
    		CloseHandle(hProcess);
    		CloseHandle(hSnapshot);
    		return TRUE;
    	}
    	return FALSE;
    }
    
    int main(int argc, char *argv[])
    {
    	UnLoad_Module(2012, "lyshark.dll");
    
    	system("pause");
    	return 0;
    }
    

    纯汇编实现远程Dll注入:

    .data
    	szMyDll        db "lyshark.dll",0h       ; 要注入的DLL
    	szDllKernel    db "Kernel32.dll",0h
    	szLoadLibrary  db "LoadLibraryA",0h
    	lpFileName     db "Tutorial-i386",0h      ; 指定要注入进程
    	lpDllName      dd ?
    .data?
    	szMyDllFull    db MAX_PATH dup (?)
    	lpLoadLibrary  dd ?
    	dwProcessID    dd ?     
    	dwThreadID     dd ?
    	hProcess       dd ?
    .code
    	main PROC
    ; 准备工作:获取dll的全路径文件名、获取LoadLibrary函数地址等
    		invoke GetCurrentDirectory,MAX_PATH,addr szMyDllFull
    		invoke lstrcat,addr szMyDllFull,addr szMyDll
    		invoke GetModuleHandle,addr szDllKernel
    		invoke GetProcAddress,eax,offset szLoadLibrary
    		mov lpLoadLibrary,eax
    
    ; 查找文件管理器窗口并获取进程ID,然后打开进程
    		invoke FindWindow,NULL,addr lpFileName
    		invoke GetWindowThreadProcessId,eax,offset dwProcessID
      		mov dwThreadID,eax
      		invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,dwProcessID
    		mov hProcess,eax
    
    ; 在进程中分配空间并将DLL文件名拷贝过去,然后创建一个LoadLibrary线程
    		invoke VirtualAllocEx,hProcess,NULL,MAX_PATH,MEM_COMMIT,PAGE_READWRITE
    		mov lpDllName,eax
    		invoke WriteProcessMemory,hProcess,eax,offset szMyDllFull,MAX_PATH,NULL
    		invoke CreateRemoteThread,hProcess,NULL,0,lpLoadLibrary,lpDllName,0,NULL
    		ret
    	main endp
    end main
    
  • 相关阅读:
    Netty NIO 框架性能压测-短链接-对比Tomcat
    Linux 下应用程序最大打开文件数的理解和修改
    kafka Failed to send messages after 3 tries 问题解决
    kafka集群搭建和使用Java写kafka生产者消费者
    如何设置jvm内存
    JVM最多能创建多少个线程:unabletocreatenewnativethread
    Eclipse 的快捷键以及文档注释、多行注释的快捷键
    react navtagion 头部有返回按钮 标题不居中解决方法
    java获取本机外网ip
    react navtagion api
  • 原文地址:https://www.cnblogs.com/LyShark/p/13424331.html
Copyright © 2011-2022 走看看