Windows Ring3层注入——使用Windows挂钩注入(七)

Windows Ring3层注入——使用Windows挂钩注入(七)

  • Windows挂钩注入原理
  • Windows挂钩注入函数原型
    • 设置钩子API函数:
    • 取消设置钩子API
    • 获取模块句柄API:
    • 继续调用钩子链中的钩子过程:
    • 钩子回调
  • Windows挂钩注入代码示例
  • Windows挂钩注入优缺点
    • 优点
    • 缺点

SetWindowsHookEx() 是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。

Windows挂钩注入原理

Windows 窗口程序是靠消息驱动的,一切过程皆是消息,原本的消息传递过程是:系统接收到消息-将消息放入系统消息队列-将消息放入线程消息队列-线程中处理该消息,而钩子函数**SetWindowsHookEx()**的作用就是拦截消息,用自定义的函数处理消息

设置全局钩子会拦截所有窗口线程的消息
设置线程钩子则只会拦截特定线程的消息

注意:当拦截当前线程消息时可以在当前代码中写回调函数,当拦截其它线程消息时则必须调用dll中回调函数
Windows Ring3层注入——使用Windows挂钩注入(七)_第1张图片

下面是利用SetWindowsHookEx函数在WinProc处理消息之前插入一个消息处理的过程,以及截获、处理和传递MSG的过程:
Windows Ring3层注入——使用Windows挂钩注入(七)_第2张图片
可以看到,MSG被新的消息处理过程接收,然后通过CallNextHookEx函数向下传递MSG。第一个CallNextHookEx如果返回"True",表示这个MSG已经被处理,如果返回"False",系统将继续把MSG传递WinProc进行处理。

Windows挂钩注入函数原型

设置钩子API函数:

HHOOK WINAPI SetWindowsHookEx(
		 int idHook,         //设置钩子的类型,意思就是我要设置的钩子是什么钩子,可以是监视窗口过程,可以是监视消息队列。
		 HOOKPROC lpfn,      //根据钩子类型.设置不同的回调函数
		 HINSTANCE hMod,     //钩子设置的Dll实例句柄,就是DLL的句柄
		 DWORD dwThreadId;   //设置钩子的线程ID. 如果为0 则设置为全局钩子.
		 )
//HHOOK 返回值. 是一个钩子过程句柄
idHook 解释
WH_CALLWNDPROC 监视到达窗口前的消息
WH_CALLWNDPROCRET 监视窗口处理后的消息
WH_DEBUG 监视系统调用其他HOOK关联的HOOK子程
WH_GETMESSAGE 监视发送到窗体消息队列里的消息
WH_JOURNALPLAYBACK 全局HOOK,可以插入消息到消息队列
WH_JOURNALRECORD 全局HOOK,监视输入事件(键盘、鼠标等)
WH_KEYBOARD 键盘钩子
WH_MOUSE 鼠标钩子
WH_MSGFILTER 监视菜单、滚动条、消息框、对话框消息和切换窗口的组合键(Alt+Tab等)
WH_SHELL 接收系统中重要的通知(如窗口被产生、摧毁等)

取消设置钩子API

BOOL WINAPI UnhookWindowsHookEx(
        HHOOK hhk            //参数是SetWindowHookEx的返回值.也就是钩子过程句柄. ;                                     
//返回值: 返回值是BOOL类型.表示设置是否成功或者失败.

获取模块句柄API:

HMODULE WINAPI GetModuleHandle(
		 LPCTSTR lpModuleName    //获取的实例句柄的文件名,可以是Dll可以使exe,如果为NULL这是当前dll/exe的实例句柄;
// 返回值 返回实例句柄.

继续调用钩子链中的钩子过程:

LRESULT WINAPI CallNextHookEx(
	      HHOOK hhk,     //保存的钩子过程,也就是SetWindowsHookEx返回值.
	      int nCode,        //根据SetWindowsHookEx设置的钩子回调而产生的不同的nCode代码. 什么意思? 意思就是如果设置的钩子类型是鼠标消息.那么那个nCode就是鼠标消息.如果是键盘这是键盘
          WPARAM wParam,    //同2参数一样.附加参数. 根据钩子回调类型.附加参数有不同的意义.比如如果是鼠标.那么这个有可能代表的就是鼠标的x位置.键盘就可能是键代码
          LPARAM lParam     //同3参数一样.附加参数.;

钩子回调

钩子回调根据SetWindowsHookEx参数1来设定的.比如如果我们设置WH_CBT 那么我们设置的回调函数就是CBT回调. 具体查询MSDN https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexa

//这个回调函数里面写我们的代码就可以了.
LRESULT CALLBACK CBTProc(         
		  int     nCode,
		  WPARAM wParam,
	      LPARAM lParam
);

Windows挂钩注入代码示例

/*注入DLL代码*/
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include 
#include 
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		{
			MessageBox(NULL , _T("DLL加载成功") ,  _T("DLL窗口提示") , IDOK);
			break;
		} 
	case DLL_THREAD_ATTACH:
		break;
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		{

			MessageBox(NULL , _T("DLL卸载成功") ,  _T("DLL窗口提示") , IDOK);
			break;
		}
	}
	return TRUE;
}

//导出回调函数
extern "C" __declspec(dllexport) LRESULT  MyMessageProcess(int Code, WPARAM wParam, LPARAM lParam)

{
	MessageBox(NULL, _T("Windows消息钩子注入成功"), _T("DLL窗口提示"), 0);
	return 0;
}


/*安装挂钩程序核心代码*/

//根据进程名称获取进程PID
DWORD GetProcessIDByName(const wchar_t* pName);
//根据进程PID获取进程主线程ID
DWORD GetThreadIdByProID(ULONG32 ulTargetProcessID);
//Windows挂钩创建
BOOL InjectDllBySetWindowsHook();


DWORD GetProcessIDByName(const wchar_t* pName)
{
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS , 0);
	if (INVALID_HANDLE_VALUE == hSnapshot) {
		return NULL;
	}
	PROCESSENTRY32 pe = { sizeof(pe) };
	for (BOOL ret = Process32First(hSnapshot, &pe) ; ret ; ret = Process32Next(hSnapshot , &pe))
	{
		if (wcscmp(pe.szExeFile , pName) == 0)
		{
			CloseHandle(hSnapshot);
			return pe.th32ProcessID;
		}
	}
	CloseHandle(hSnapshot);
	return 0;
}

DWORD GetThreadIdByProID(ULONG32 ulTargetProcessID)
{	
	HANDLE Handle = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
	if (Handle != INVALID_HANDLE_VALUE)	
	{		THREADENTRY32 te;		
			te.dwSize = sizeof(te);
	if (Thread32First(Handle, &te))
	{			
		do			
		{				
			if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))
			{					
				if (te.th32OwnerProcessID == ulTargetProcessID)					
				{						
					HANDLE hThread = OpenThread(READ_CONTROL, FALSE, te.th32ThreadID);						
					if (!hThread)						
					{							
						OutputDebugString(_T("主线程句柄获取失败"));
					}						
					else						
					{							
						return te.th32ThreadID;						
					}					
				}				
			}			
		} while (Thread32Next(Handle, &te));		
	}	
	}	CloseHandle(Handle);	
	return 0;
}

BOOL InjectDllBySetWindowsHook()
{
	HMODULE m_hmDll = LoadLibrary(_T("WindowsHookInjectDLL.dll"));
	if (m_hmDll == NULL)
	{
		OutputDebugString(_T("DLL加载失败"));
		return FALSE;
	}
	
	//获取DLL中到处函数的地址
	HOOKPROC sub_address = NULL;
	sub_address = (HOOKPROC)GetProcAddress(m_hmDll,"MyMessageProcess");
	if(sub_address == NULL)
	{
		OutputDebugString(_T("获取函数地址失败"));
		return FALSE;
	}

	DWORD hPid;
	hPid=GetProcessIDByName(_T("notepad++.exe"));
	if(hPid == NULL)
	{
		OutputDebugString(_T("获取目标进程PID失败"));
		return FALSE;

	}

	DWORD dwThreadID;
	dwThreadID = GetThreadIdByProID(hPid);
	if(dwThreadID == NULL)
	{
		OutputDebugString(_T("获取目标进程主线程句柄失败"));
		return FALSE;
	}

	m_hHook = SetWindowsHookEx(WH_KEYBOARD , sub_address , m_hmDll , dwThreadID);
	if(m_hHook == NULL)
	{
		OutputDebugString(_T("建立钩子失败"));
		return FALSE;
	}

	return TRUE;
}

Windows挂钩注入优缺点

优点

代码和原理简单,易于实现,可以对所有消息循环的程序进行注入。

缺点

容易被发现和防御,对没有消息循环的纯后台程序无能为力。

你可能感兴趣的:(windows注入,c++,安全)