Windows挂钩注入DLL

代码实现(被注入进程):

private:
	DWORD m_dwId;
	HHOOK m_hHook;
	HMODULE m_hmDll;
private:
	DWORD GetPIdByProcessName(const char* pszProcessName);
	BOOL InjectDllBySetWindowsHook(ULONG32 ulTargetProcessID,char* pszDllName);
	DWORD GetThreadID(ULONG32 ulTargetProcessID);
/*获取ID按钮*/
void CInjectDLLDlg::OnBnClickedBtnGetid()
{
	char szProName[MAX_PATH] = {0};

	GetDlgItemText(IDC_ED_NAME,szProName,MAX_PATH);
	if(strstr(szProName,".exe") == NULL)
		strcat_s(szProName,MAX_PATH * sizeof(char),".exe");

	m_dwId = GetPIdByProcessName(szProName);

	SetDlgItemInt(IDC_ED_ID,(int)m_dwId,FALSE);
}

/*Windows挂钩DLL注入*/
DWORD CInjectDLLDlg::GetPIdByProcessName(const char* pszProcessName)
{
	DWORD id = 0;   
	//获得系统快照句柄 (得到当前的所有进程)   
	HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0) ;   
	PROCESSENTRY32 pInfo; //用于保存进程信息的一个数据结构   
	pInfo.dwSize = sizeof(pInfo);   
	//从快照中获取进程列表   
	Process32First(hSnapShot, &pInfo) ; //从第一个进程开始循环   
	do   
	{   
		//这里的 pszProcessName 为你的进程名称   
		if(strcmp(strlwr(_strdup(pInfo.szExeFile)), pszProcessName) == 0)   
		{   
			id = pInfo.th32ProcessID ;   
			break ;   
		}   
	}while(Process32Next(hSnapShot, &pInfo) != FALSE);   
	return id; 
}

/*Windows挂钩Dll注入 按钮*/
void CInjectDLLDlg::OnBnClickedOk()
{
	char szDllName[MAX_PATH] = {0};
	GetDlgItemText(IDC_ED_DLLNAME,szDllName,MAX_PATH);
	BOOL bRet = InjectDllBySetWindowsHook((ULONG32)m_dwId,szDllName);
	if (bRet)
	{
		//MessageBox("注入成功");
	}
}

/*进程注入*/
BOOL CInjectDLLDlg::InjectDllBySetWindowsHook(ULONG32 ulTargetProcessID,char* pszDllName)
{
	HMODULE m_hmDll = LoadLibrary(pszDllName);
	if (NULL == m_hmDll)
	{
		MessageBox("LoadLibraryError!");
		return FALSE;
	}

	HOOKPROC sub_address = NULL;
	sub_address = (HOOKPROC)GetProcAddress(m_hmDll,"MyMessageProcess");
	if (NULL == sub_address)
	{
		MessageBox("GetProcAddressError!");
		return FALSE;
	}

	DWORD dwThreadID = GetThreadID(ulTargetProcessID);

	/*
	    参数1:要安装的挂钩类型
		参数2:指定系统调用的窗口消息处理函数
		参数3:标示一个包含窗口处理消息函数(参数2)的DLL
		参数4:安装挂钩的线程ID
	*/
	m_hHook = SetWindowsHookEx(WH_KEYBOARD,
		                           sub_address,
								   m_hmDll,
								   dwThreadID);
}
/*获取进程的主线程ID*/
DWORD CInjectDLLDlg::GetThreadID(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)
						{
							printf("Couldn't get thread handle\r\n");
						}
						else
						{
							return te.th32ThreadID;
						}
					}
				}
			} while (Thread32Next(Handle, &te));
		}
	}
	CloseHandle(Handle);
	return (DWORD)0;
}

/*退出按钮*/
void CInjectDLLDlg::OnBnClickedCancel()
{
	if(m_hHook)
		UnhookWindowsHookEx(m_hHook);
	if(m_hmDll)
		FreeLibrary(m_hmDll);
	CDialogEx::OnCancel();
}

Dll:

#ifdef MyDll
#else
#define MyDll extern "C" __declspec(dllimport)
#endif



MyDll LRESULT MyMessageProcess(int Code, WPARAM wParam, LPARAM lParam);
#include "stdafx.h"

#define MyDll extern "C" __declspec(dllexport)
#include "MyDll.h"

MyDll LRESULT MyMessageProcess(int Code, WPARAM wParam, LPARAM lParam)
{
	MessageBoxA(NULL, "GetMessage!", "Message", 0);
	return 0;
}

////////////////////////////////////////////////////引用////////////////////////////////////////////////////////////////

用windows钩子将dll注入到其它进程地址空间中,然后做些自己想做的事情,比如搞点破坏什么的,这些都是可以的。关于这节的理论部分不是特别好说,所以,就通过例子以及我在这个过程中遇到的问题来讲述。
 
一般情况,我们会说windows为每个进程提供了4GB(32位系统)的地址空间,而且每个进程的地址空间是独立的。也就说,进程A是不能访问进程B的地址空间中的内容的。这样做有一个好处,就是安全!但是windows也给我们提供了一点后门,可以让进程A对进程B做点什么……这里我所要讲到的windows钩子就是其方法一种。
 
windows钩子的原理就是首先设置一个钩子,这个钩子可以是全局的,也可以是针对某个线程的。设置钩子的时候SetWindowHookEx() 可以设置一些属性。说的通俗点,就是你想钩住什么东西,消息?比如:键盘消息、鼠标消息等等。
 
HHOOK SetWindowsHookEx( int idHook, // hook type 钩子的类型,消息钩子还是键盘钩子
HOOKPROC lpfn, // hook procedure钩子函数地址
HINSTANCE hMod, // handle to application instance钩子所在的dll
DWORD dwThreadId // thread identifier线程ID );
钩子的种类是很多的,这个可以通过MSDN自己查看,第三个参数是钩子函数所在的dll模块,如果钩子函数是要钩当前进程,那么DLL必须为NULL,dwThreadId是要钩住哪个线程,如果其值为0,表示给系统中所有的GUI线程都安装钩子。

为了便于理解,我假定设置的是WH_GETMESSAGE 类型的全局钩子。然后我要截获某个GUI窗口的消息。消息通过GetMessage()之后会派送到相应的WndProc函数。但是为了截获这些消息,我肯定得在消息被送到WndProc之前处理它。由于我们安装了钩子,所以每当有消息产生的时候会调用GetMsgProc函数,然后再通过在GetMsgProc中可以通过SetWindowLongPtr()将窗口子类化,当然,子类窗口的窗口过程必须与GetMsgProc在同一个DLL中。

/////////////////////////Hook.dll///////////////////////////////////
#include
HWND g_hWnd=NULL;
HHOOK hHook;
WNDPROC lpOldProc;
HMODULE hDLL;
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD dwReason, LPVOID lpvReserved)
{
 hDLL = hinstDLL;
 return TRUE;
}     
LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
 if ( uMsg ==WM_COMMAND )
 {
 }
 else if(uMsg ==WM_MOVE)
 { 
  MessageBox(NULL,TEXT("WM_MOVE msg triggered"),TEXT("提醒"),MB_OK);
 }
 return CallWindowProc(lpOldProc,hwnd,uMsg,wParam,lParam);
}
LRESULT CALLBACK HookProc(int nCode,WPARAM wParam,LPARAM lParam)
{
 static  BOOL isFirst = TRUE;
 if(isFirst)
 {
  
  HWND hWindow=FindWindow(NULL,TEXT("成绩计算器"));
  g_hWnd=hWindow;
  if(hWindow==NULL) {isFirst =FALSE;return 0;}
  else
  {
   lpOldProc = (WNDPROC)SetWindowLongPtr(g_hWnd,GWLP_WNDPROC,(LONG_PTR)SubclassWndProc);
  }
 }
 return CallNextHookEx(hHook,nCode, wParam, lParam);
}
extern"C" __declspec(dllexport) void SetHook()
{
  hHook =  SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)HookProc,hDLL,0);
}
这个是我写的程序的一个例子。首先主程序加载该dll,然后调用SetHook函数设置一个全局钩子。然后当有任何消息的时候HookProc会被调用,在HookProc,我们找到想要监听的窗口“成绩计算器”的句柄,然后通过SetWindowLongPtr子类化这个窗口(函数返回值是原窗口过程的地址),这样新的窗口过程处理函数SubclassWndProc就会替代原来的窗口过程。窗口所有消息的处理将有SubclassWndProc完成。
在这里有两点需要注意:
1)在HookProc中,需要进行一个判断。这样确保SetWindowLongPtr只调用一次。我刚开始的时候没有处理这里所以动不动机器就死机了,也出现过每次运行我的主程序,“成绩计算器”窗口就会消失。后来才意识到这个地方会多次调用,当然也得感谢高手的帮助。
2)在SubclassWndProc中必须将不处理的消息还给原来的窗口,也就是必须调用CallWindowProc(lpOldProc,hwnd,uMsg,wParam,lParam);这个应该很好理解。我刚开始时还以为是像着样子调用呢,lpOldProc(hwnd,uMsg,wParam,lParam),这种方式是错误的。
 
总的来说,windows钩子注入DLL就是对SetWindowsHook,和SetWindowLongPtr的组合使用。但是他的灵活性不好,不如后面我要见到的远程线程注入Dll的方式灵活。



猜你喜欢

转载自blog.csdn.net/u012372584/article/details/79817976