Windows黑客编程之全局钩子

描述

  • 通过全局钩子监听windows消息,消息发生时,系统会将包含钩子回调函数的dll注入到所有进程中

准备知识

利用全局钩子进行dll注入

  • windows的钩子机制用于监听系统消息并做出反应,分为局部钩子和全局钩子,局部钩子是针对某个线程的,全局钩子作用于整个系统基于消息的应用
  • 全局钩子必须写在一个dll中,钩子安装成功后,一旦发生事件,会将包含钩子回调代码的dll加载到进程的内存空间中

dll的两种导出方式

  • 利用def文件导出
  • 利用declspec关键字导出
  • 注意,如果dll不导出数据的话,生成时不会产生lib文件

全局/静态/外部变量和函数

  • 全局变量和函数作用域是整个程序,在不同的cpp文件里定义同一个全局变量会报错冲突,写在函数外的变量默认是全局的
  • 静态变量和函数作用域仅限当前cpp文件
  • 外部变量和函数声明,表示当前变量和函数在别的cpp文件里被定义了,函数默认是外部的

调用其他文件中的函数和变量

  • 有两种方式:头文件和extern
  • 使用头文件调用,函数和变量必须在头文件中声明和定义
  • 使用extern关键字调用,函数和变量在cpp或者c文件中声明和定义

预编译头文件

  • 预编译头是程序设计时把头文件编译为中间格式(如目标文件),以节约在开发过程中编译器反复编译该头文件的开销
  • 比如stdafx.h文件中,引用常用的windws.h等大量头文件,只用编译一次,后续编译使用已生成的目标文件
  • 为了避免同一个头文件被包含(include)多次,C/C++中有两种宏实现方式:一种是#ifndef方式;另一种是#pragma once方式
  • 可以在vs属性设置中选择使用/不使用编译头,dll工程默认是使用的
  • 更多关于预编译头

typedef声明

  • 可以为复杂声明定义一个简单的别名
  • 记住两个模式
    • typedef()() 函数指针
    • typedef()[] 数组指针
  • 例子
    原声明:int *(*a[5])(int, char*);
    变量名为a,直接用一个新别名pFun替换a就可以了:
    typedef int *(*pFun)(int, char*);
    原声明的最简化版:pFun a[5];
    

代码

  • 分为钩子程序和钩子安装程序

钩子程序

  • 建立dll工程,取消pch预编译头,在dllMain.cpp中获取声明module全局变量并获取地址
  • 再新建一个globalhook.cpp文件,导出钩子函数,并定义钩子回调函数
LRESULT GetMsgProc(
	int code,
	WPARAM wParam,
	LPARAM lParam)
{
		return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}

BOOL SetGlobalHook()
{
	g_hHook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);
	if (NULL == g_hHook)
	{
		return FALSE;
	}
	return TRUE;
}

BOOL UnsetGlobalHook()
{
	if (g_hHook)
	{
		::UnhookWindowsHookEx(g_hHook);
	}
	return TRUE;
}
  • 关于如何导出dll函数,本文采用的是def导出,特别要注意def文件不是自己取名的,是要右键项目添加新项,选择代码中的模板定义文件def,然后在里面写入下面的代码
LIBRARY

EXPORTS
SetGlobalHook
UnsetGlobalHook

如果是自己创建文件然后取名def,则无法导出函数,下图中用dumpbin工具查看导出函数,上面那部分导出失败,下面部分导出成功

  • 回调函数中用到钩子句柄g_hHook,因此需要将其从一个进程传递到其它进程,通过设置共享内存来完成值传递
#pragma data_seg("hookdata")
	HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:hookdata,RWS")

钩子安装程序

  • loadlibrary加载钩子dll,获取导出函数的api,返回函数指针,然后解引用调用
#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
	typedef BOOL(*typedef_SetHook)();
	typedef BOOL(*typedef_UnsetHook)();
	HMODULE hDll = NULL;
	typedef_SetHook SetGlobalHook = NULL;
	typedef_UnsetHook UnsetGlobalHook = NULL;
	BOOL bRet = FALSE;

	do
	{
		hDll = ::LoadLibrary("GlobalHook.dll");
		if (NULL == hDll)
		{
			printf("LoadLibrary Error[%d]\n", ::GetLastError());
			break;
		}
		SetGlobalHook = (typedef_SetHook)::GetProcAddress(hDll, "SetGlobalHook");
		if (NULL == SetGlobalHook)
		{
			printf("GetProcAddress Error[%d]\n", ::GetLastError());
			break;
		}
		bRet = SetGlobalHook();
		if (bRet)
		{
			printf("SetGlobalHook OK.\n");
		}
		else
		{
			printf("SetGlobalHook Error\n");
		}

		system("pause");

		UnsetGlobalHook = (typedef_UnsetHook)::GetProcAddress(hDll, "UnsetGlobalHook");
		if (NULL == UnsetGlobalHook)
		{
			printf("GetProcAddress Error[%d]\n", ::GetLastError());
		}
		UnsetGlobalHook();
		printf("UnsetGlobalHook OK.\n");
	} while (FALSE);

	system("pause");
	return 0;
}

结果

  • 找了台win10虚拟机做测试,安装钩子后,打开processexplorer查看进程加载dll情况,随便找一个procexp64.exe进程,可以看到加载了GlobalHook.dll,注入成功!
  • 卸载钩子后,可以看到GlobalHook.dll成功释放
posted @   z5onk0  阅读(325)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示