10 dll注入

概念      

  在Windows操作系统中,运行的每一个进程都生活在自己的程序空间中(保护模式),每一个进程都认为自己拥有整个机器的控制权,每个进程都认为自己拥有计算机的整个内存空间,这些假象都是操作系统创造的(操作系统控制CPU使得CPU启用保护模式)。理论上而言,运行在操作系统上的每一个进程之间都是互不干扰的,即每个进程都会拥有独立的地址空间。比如说进程B修改了地址为0x4000000的数据,那么进程C的地址为0x4000000处的数据并未随着B的修改而发生改变,并且进程C可能并不拥有地址为0x4000000的内存(操作系统可能没有为进程C映射这块内存)。因此,如果某进程有一个缺陷覆盖了随机地址处的内存(这可能导致程序运行出现问题),那么这个缺陷并不会影响到其他进程所使用的内存。
  也正是由于进程的地址空间是独立的(保护模式),因此我们很难编写能够与其它进程通信或控制其它进程的应用程序。
  所谓的dll注入即是让程序A强行加载程序B给定的a.dll,并执行程序B给定的a.dll里面的代码。注意,程序B所给定的a.dll原先并不会被程序A主动加载,但是当程序B通过某种手段让程序A“加载”a.dll后,程序A将会执行a.dll里的代码,此时,a.dll就进入了程序A的地址空间,而a.dll模块的程序逻辑由程序B的开发者设计,因此程序B的开发者可以对程序A为所欲为。

     应用程序一般会在以下情况使用dll注入技术来完成某些功能:
    1.为目标进程添加新的“实用”功能;
    2.需要一些手段来辅助调试被注入dll的进程;
    3.为目标进程安装钩子程序(API Hook);

进程地址空间

DLL注入

注入方式

注册表

打开注册表

win+r 输入  regedit(register + edit 注册表编辑器)

一、什么是注册表

   注册表是windows操作系统、硬件设备以及客户应用程序得以正常运行和保存设置的核心“数据库”,也可以说是一个非常巨大的树状分层结构的数据库系统。
    注册表记录了用户安装在计算机上的软件和每个程序的相互关联信息,它包括了计算机的硬件配置,包括自动配置的即插即用的设备和已有的各种设备说明、状态属性以及各种状态信息和数据。利用一个功能强大的注册表数据库来统一集中地管理系统硬件设施、软件配置等信息,从而方便了管理,增强了系统的稳定性。
二、注册表的功能
    刚才我们看到了,注册表中记录了用户安装在计算机上的软件和每个程序的相关信息,通过它可以控制硬件、软件、用户环境和操作系统界面的数据信息文件。
      相关知识:注册表文件的数据信息保存在system.dat和user.dat中、利用regedit.exe程序能够存取注册表文件

根键:这个称为HKEY…………,某一项的句柄项:附加的文件夹和一个或多个值
子项:在某一个项(父项)下面出现的项(子项)
值项:带有一个名称和一个值的有序值,每个项都可包括任何数量的值项,值项由三个部分组成:名称、数据类型和数据。
1、名称:不包括反斜线的字符、数字、代表符和空格的任意组合。同一键中不可有相同的名称
2、数据类型:包括字符串、二进制和双字节等
3、数据:值项的具体值,它的大小可以占用64KB

注册表包括以下5个根键
1.HKEY_CLASSES_ROOT
说明:该根键包括启动应用程序所需的全部信息,包括扩展名,应用程序与文档之间的关系,驱动程序名,DDE和OLE信息,类ID
编号和应用程序与文档的图标等。
2.HKEY_CURRENT_USER
说明:该根键包括当前登录用户的配置信息,包括环境变量,个人程序以及桌面设置等
3.HKEY_LOCAL_MACHINE
说明:该根键包括本地计算机的系统信息,包括硬件和操作系统信息,安全数据和计算机专用的各类软件设置信息
4.HKEY_USERS
说明:该根键包括计算机的所有用户使用的配置数据,这些数据只有在用户登录系统时才能访问。这些信息告诉系统当前用户使
用的图标,激活的程序组,开始菜单的内容以及颜色,字体
5.HKEY_CURRENT_CONFIG
说明:该根键包括当前硬件的配置信息,其中的信息是从HKEY_LOCAL_MACHINE中映射出来的。

 

目标键值:AppInit_DLLs


HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\
AppInit_DLLs     LoadAppInit_DLLs

        AppInit_DLLs用来全局注入dll模块,凡是导入了user32.dll的程序都会 主动加载这个键值下的模块

        AppInit_Dlls键值位于注册表 HKLM\Microsoft\Windows NT\CurrentVersion\Windows下面,相对于其他的注册表启动项来说,这个键值的特殊之处在于任何使用到User32.dll 的EXE、DLL、OCX等类型的PE文件都会读取这个地方,并且根据约定的规范将这个键值下指向的DLL文件进行加载,加载的方式是调用 LoadLibrary。

        AppInit_Dlls的键值是一个非常危险的键值,从某种程度上来说,这是一个Windows最容易被人利用的漏洞,因为只要有任何的恶意软件在这里进行了修改,那么就意味着任何使用到User32.DLL的进程都会被AppInit_DLLs指向的DLL所注入。因为进程内部的DLL是共享整个进程空间的,因此意味着进程里面的DLL是可以控制整个进程的行为的。由于User32.DLL是一个非常非常通用的DLL,它提供了大多数Win32用户界面、消息相关的功能,只有极少数的程序不会使用User32.DLL,因此一旦有恶意软件修改了AppInit_Dlls键值,那么整个系统都有可能处于非常危险的状态。

        众所周知,Windows服务程序的启动时机是可以非常早的,往往在用户登录之前就完成启动了,而这个时候最常见的Run键值还不一定被处理完,而且Windows服务程序拥有相当高的权限(默认是Local System,可以对系统里面所有的资源进行操作),因此如果一个恶意软件被加载到Windows服务里面,那么是会非常危险的。前文提到,任何进程使用了User32.DLL,都会对AppInit_Dlls键值指向的DLL进行加载,如果是一个Windows服务程序,也不例外!

        由于AppInit_Dlls是一种系统全局性的Hook(system-wide hook),要规避此类的Hook的确很困难,虽然使用驱动程序进行保护能够规避此类问题,但也不是非要使用驱动程序进行处理的。前文说过,只有当使用到User32.DLL这个模块的时候才会触发读取AppInit_Dlls指向的DLL,如果不使用User32.DLL,那么AppInit_Dlls是不会被使用到的。但是要让一个程序不使用User32.DLL会变得非常困难(命令行窗口没有使用User32.DLL),因为任何的窗口、消息都和这个模块有关,为了保证有良好的用户体验,100%的窗口程序都和这个模块有关。从开发角度来说,最好的一种解决办法就是将程序功能逻辑和界面逻辑完全分离,功能逻辑模块负责功能,界面逻辑模块负责界面显示,2者之间采用IPC机制进行交互,功能逻辑模块不依靠User32.DLL,,而且作为独立进程进行处理,这样就可以规避AppInit_Dlls造成的Hook了。

        类似的Windows安全相关的缺陷点其实还有很多的,这些缺陷点的来源是为了保证向下的兼容性。相信MIcrosoft已经发现并正在修补这些地方,从Windows Vista上可以看到,AppInit_Dlls的键值在Windows Vista上是不起作用的,因此在Windows Vista里面,这个键值已经被抛弃了(改用另外一个键值执行类似的功能,但是增加了基于UAC的安全防护)。

/*
内容:windows注册表API
启动注册表:regedit
目标键值:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\
AppInit_DLLs     LoadAppInit_DLLs
API:RegOpenKeyEx RegSetValueEx RegQueryValueEx RegCloseKey
注册表根:
HKEY_CLASSES_ROOT
HKEY_LOCAL_MACHINE
HKEY_CURRENT_USER
HKEY_USERS
HKEY_CURRENT_CONFIG
权限:KEY_READ KEY_WRITE KEY_EXECUTE KEY_ALL_ACCESS
涉及的数据类型:REG_SZ REG_DWORD

 */
#include <windows.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
	HKEY key;
	DWORD LoadAppInitVal = 1;
	WCHAR AppInitVal[] = L"test11.dll";
	WCHAR subKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
	LSTATUS ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, KEY_ALL_ACCESS, &key);
	if (ret != ERROR_SUCCESS)
	{
		printf("open key failed....\n");
		getchar();
		return 0;
	}

	ret = RegSetValueEx(key, L"AppInit_DLLs", 0, REG_SZ, (const BYTE*)AppInitVal, (wcslen(AppInitVal) + 1) * sizeof(WCHAR));
	if (ret != ERROR_SUCCESS)
	{
		printf("set key failed....\n");
		getchar();
		return 0;
	}

	ret = RegSetValueEx(key, L"LoadAppInit_DLLs", 0, REG_DWORD, (const BYTE*)&LoadAppInitVal, sizeof(DWORD));
	if (ret != ERROR_SUCCESS)
	{
		printf("set key failed....\n");
		getchar();
		return 0;
	}

	RegCloseKey(key);

	getchar();
	return 0;
}

 

挂钩注入dll

 

基本概念

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

  钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。

运行机制

  1、钩子链表和钩子子程:

  每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针 指向指定的,应用程序定义的,被Hook子程调用的回调函数,也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。 一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。

  Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。

  钩子子程是一个应用程序定义的回调函数(CALLBACKFunction),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。

回调函数必须按照以下的语法

LRESULT CALLBACK HookProc
(
int nCode,
WPARAM wParam,
LPARAM lParam
);

  HookProc是回调函数名。

  nCode参数是Hook代码,Hook子程使用这个参数来确定任务。这个参数的值依赖于Hook类型,每一种Hook都有自己的Hook代码特征字符集。

  wParam和lParam参数的值依赖于Hook代码,但是它们的典型值是包含了关于发送或者接收消息的信息。

  2、钩子的安装与释放:

  使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子 链表中。SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时,系统就调用与这个Hook关联的 Hook链的开头的Hook子程。每一个Hook链中的Hook子程都决定是否把这个事件传递到下一个Hook子程。Hook子程传递事件到下一个 Hook子程需要调用CallNextHookEx函数。

  HHOOK SetWindowsHookEx(
  int idHook, // 钩子的类型,即它处理的消息类型
  HOOKPROC lpfn, //钩子子程的地址指针。如果dwThreadId参数为0,或是一个由别的进程创建的线程的标识,lpfn必须指向DLL中的钩子子程。除此以外,lpfn可以指向当前进程的一段钩子子程代码。钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。
  HINSTANCE hMod, //应用程序实例的句柄。标识包含lpfn所指的子程的DLL 如果dwThreadId 标识当前进程创建的一个线程,而且子程代码位于当前进程,hMod必须为NULL。可以很简单的设定其为本应用程序的实例句柄。
  DWORD dwThreadId //与安装的钩子子程相关联的线程的标识符。 如果为0,钩子子程与所有的线程关联,即为全局钩子。
  );


钩子类型

  每一种类型的Hook可以使应用程序能够监视不同类型的系统消息处理机制。下面描述所有可以利用的Hook类型。

  1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks

  WH_CALLWNDPROC和WH_CALLWNDPROCRETHooks使你可以监视发送到窗口过程的消息。系统在消息发送到接收窗口过程之前调用WH_CALLWNDPROCHook子程,并且在窗口过程处理完消息之后调用WH_CALLWNDPROCRET Hook子程。

  WH_CALLWNDPROCRETHook传递指针到CWPRETSTRUCT结构,再传递到Hook子程。

  CWPRETSTRUCT结构包含了来自处理消息的窗口过程的返回值,同样也包括了与这个消息关联的消息参数。

  2、WH_CBT Hook

  在以下事件之前,系统都会调用WH_CBT Hook子程,这些事件包括:

  1)激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件;

  2)完成系统指令;

  3)来自系统消息队列中的移动鼠标,键盘事件;

  4)设置输入焦点事件;

  5)同步系统消息队列事件。

  Hook子程的返回值确定系统是否允许或者防止这些操作中的一个。

  3、WH_DEBUG Hook

  在系统调用系统中与其他Hook关联的Hook子程之前,系统会调用WH_DEBUG Hook子程。你可以使用这个Hook来决定是否允许系统调用与其他Hook关联的Hook子程。

  4、WH_FOREGROUNDIDLE Hook

  当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLEHook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就会调用WH_FOREGROUNDIDLE Hook子程。

  5、WH_GETMESSAGE Hook

  应用程序使用WH_GETMESSAGE Hook来监视从GetMessage orPeekMessage函数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及其他发送到消息队列中的消息。

  6、WH_JOURNALPLAYBACK Hook

  WH_JOURNALPLAYBACKHook使应用程序可以插入消息到系统消息队列。可以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠标和键盘事件。只要WH_JOURNALPLAYBACKHook已经安装,正常的鼠标和键盘事件就是无效的。

  WH_JOURNALPLAYBACKHook是全局Hook,它不能象线程特定Hook一样使用。

  WH_JOURNALPLAYBACKHook返回超时值,这个值告诉系统在处理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实时事件的回放。

  WH_JOURNALPLAYBACK是system-wide localhooks,它们不会被注射到任何行程位址空间。

  7、WH_JOURNALRECORD Hook

  WH_JOURNALRECORDHook用来监视和记录输入事件。典型的,可以使用这个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook来回放。

  WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样使用。

  WH_JOURNALRECORD是system-wide local hooks,它们不会被注射到任何行程位址空间。

  8、WH_KEYBOARD Hook

  在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and WM_KEYUP消息,这些消息通过GetMessage or PeekMessagefunction返回。可以使用这个Hook来监视输入到消息队列中的键盘消息。

  9、WH_KEYBOARD_LL Hook

  WH_KEYBOARD_LLHook监视输入到线程消息队列中的键盘消息。

  10、WH_MOUSE Hook

  WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。使用这个Hook监视输入到消息队列中的鼠标消息。

  11、WH_MOUSE_LL Hook

  WH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。

  12、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks

  WH_MSGFILTER 和 WH_SYSMSGFILTERHooks使我们可以监视菜单,滚动条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。WH_MSGFILTERHook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通过安装了Hook子程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook监视所有应用程序消息。

  WH_MSGFILTER 和 WH_SYSMSGFILTERHooks使我们可以在模式循环期间过滤消息,这等价于在主消息循环中过滤消息。

  通过调用CallMsgFilterfunction可以直接的调用WH_MSGFILTERHook。通过使用这个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循环里一样。

  13、WH_SHELL Hook

  外壳应用程序可以使用WH_SHELLHook去接收重要的通知。当外壳应用程序是激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子程。

  WH_SHELL 共有5钟情况:

  1)只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁;

  2)当Taskbar需要重画某个按钮;

  3)当系统需要显示关于Taskbar的一个程序的最小化形式;

  4)当目前的键盘布局状态改变;

  5)当使用者按Ctrl+Esc去执行TaskManager(或相同级别的程序)。

  按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接收WH_SHELL消息之前,应用程序必须调用SystemParametersInfofunction注册它自己。

  函数成功则返回钩子子程的句柄,失败返回NULL。

  以上所说的钩子子程与线程相关联是指在一钩子链表中发给该线程的消息同时发送给钩子子程,且被钩子子程先处理。

  在钩子子程中调用得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外一个SDK中的API函数CallNextHookEx来传递它,以执行钩子链表所指的下一个钩子子程。这个函数成功时返回钩子链中下一个钩子过程的返回值,返回值的类型依赖于钩子的类型。这个函数的原型如下:

  LRESULT CallNextHookEx
  (
  HHOOK hhk;
   int nCode;
  WPARAM wParam;
  LPARAM lParam;
  );

  hhk为当前钩子的句柄,由SetWindowsHookEx()函数返回。

  NCode为传给钩子过程的事件代码。

  wParam和lParam 分别是传给钩子子程的wParam值,其具体含义与钩子类型有关。

  钩子函数也可以通过直接返回TRUE来丢弃该消息,并阻止该消息的传递。否则的话,其他安装了钩子的应用程序将不会接收到钩子的通知而且还有可能产生不正确的结果。

  钩子在使用完之后需要用UnHookWindowsHookEx()卸载,否则会造成麻烦。释放钩子比较简单,UnHookWindowsHookEx()只有一个参数。函数原型如下:

  UnHookWindowsHookEx(HHOOK hhk);

  函数成功返回TRUE,否则返回FALSE。

  3、一些运行机制:

  Win32环境中,情DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入DLL时,操作系统自动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。

  因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个 Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。

  #pragma data_seg预处理指令用于设置共享数据段。例如:

  #pragmadata_seg("SharedDataName")

  HHOOK hHook=NULL;

  #pragma data_seg()

  在#pragmadata_seg("SharedDataName")和#pragma data_seg()之间的所有变量将被访问该Dll的所有进程看到和共享。再加上一条指令#pragmacomment(linker,"/section:.SharedDataName,rws"),那么这个数据节中的数据可以在所有DLL的实例之间共享。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。

  当进程隐式或显式调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里(以下简称"地址空间")。这使得DLL成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。

  4、系统钩子与线程钩子:

  SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。

  线程勾子用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内。

  系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。

几点说明

  (1)如果对于同一事件(如鼠标消息)既安装了线程勾子又安装了系统勾子,那么系统会自动先调用线程勾子,然后调用系统勾子。

  (2)对同一事件消息可安装多个勾子处理过程,这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。

  (3)勾子特别是系统勾子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾子,在使用完毕后要及时卸载。

线程钩子

/*
内容:直接挂钩到应用程序
API:SetWindowsHookEx  UnhookWindowsHookEx

HHOOK WINAPI SetWindowsHookEx(
	int       idHook,
	HOOKPROC  lpfn,
	HINSTANCE hMod,
	DWORD     dwThreadId
);

BOOL WINAPI UnhookWindowsHookEx(
	HHOOK hhk
);


触发:通过按键触发挂钩和解挂钩
七种事件消息:
WH_CALLWNDPROC <--> CallWndProc
WH_CBT <--> CBTProc
WH_DEBUG <--> DebugProc
WH_GETMESSAGE <--> GetMsgProc
WH_KEYBOARD <--> KeyboardProc
WH_MOUSE <--> MouseProc
WH_MSGFILTER <--> MessageProc
*/

//HookAPP.cpp: 定义应用程序的入口点。
//

#include "stdafx.h"
#include "HookAPP.h"

typedef struct _MYHOOKDATA
{
	int nType;
	HOOKPROC hkprc;
	HHOOK hhook;
}MYHOOKDATA;

MYHOOKDATA myhookdata[7];

LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI CBTProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI DebugProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI MouseProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI MessageProc(int nCode, WPARAM wParam, LPARAM lParam);

#define MAX_LOADSTRING 100

// 全局变量: 
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

// 此代码模块中包含的函数的前向声明: 
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_HOOKAPP, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化: 
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_HOOKAPP));

    MSG msg;

    // 主消息循环: 
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HOOKAPP));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_HOOKAPP);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释: 
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
	case WM_CREATE:
		OutputDebugString(L"hookapp WM_CREATE");
		myhookdata[0].nType = WH_CALLWNDPROC;
		myhookdata[0].hkprc = CallWndProc;
		myhookdata[1].nType = WH_CBT;
		myhookdata[1].hkprc = CBTProc;
		myhookdata[2].nType = WH_DEBUG;
		myhookdata[2].hkprc = DebugProc;
		myhookdata[3].nType = WH_GETMESSAGE;
		myhookdata[3].hkprc = GetMsgProc;
		myhookdata[4].nType = WH_KEYBOARD;
		myhookdata[4].hkprc = KeyboardProc;
		myhookdata[5].nType = WH_MOUSE;
		myhookdata[5].hkprc = MouseProc;
		myhookdata[6].nType = WH_MSGFILTER;
		myhookdata[6].hkprc = MessageProc;
		break;

    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择: 
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;

	case WM_CHAR:
		switch (wParam)
		{
		case 'a':
			OutputDebugString(L"hookapp HOOK WH_CALLWNDPROC");
			myhookdata[0].hhook = SetWindowsHookEx(
				myhookdata[0].nType,
				myhookdata[0].hkprc,
				(HINSTANCE)NULL,
				GetCurrentThreadId());
			break;
		case 'b':
			OutputDebugString(L"hookapp UNHOOK WH_CALLWNDPROC");
			UnhookWindowsHookEx(myhookdata[0].hhook);
			break;
		case 'c':
			OutputDebugString(L"hookapp HOOK WH_CBT");
			myhookdata[1].hhook = SetWindowsHookEx(
				myhookdata[1].nType,
				myhookdata[1].hkprc,
				(HINSTANCE)NULL,
				GetCurrentThreadId());
			break;
		case 'd':
			OutputDebugString(L"hookapp UNHOOK WH_CBT");
			UnhookWindowsHookEx(myhookdata[1].hhook);
			break;
		case 'e':
			OutputDebugString(L"hookapp HOOK WH_DEBUG");
			myhookdata[2].hhook = SetWindowsHookEx(
				myhookdata[2].nType,
				myhookdata[2].hkprc,
				(HINSTANCE)NULL,
				GetCurrentThreadId());
			break;
		case 'f':
			OutputDebugString(L"hookapp UNHOOK WH_DEBUG");
			UnhookWindowsHookEx(myhookdata[2].hhook);
			break;
		case 'g':
			OutputDebugString(L"hookapp HOOK WH_GETMESSAGE");
			myhookdata[3].hhook = SetWindowsHookEx(
				myhookdata[3].nType,
				myhookdata[3].hkprc,
				(HINSTANCE)NULL,
				GetCurrentThreadId());
			break;
		case 'h':
			OutputDebugString(L"hookapp UNHOOK WH_GETMESSAGE");
			UnhookWindowsHookEx(myhookdata[3].hhook);
			break;
		case 'i':
			OutputDebugString(L"hookapp HOOK WH_KEYBOARD");
			myhookdata[4].hhook = SetWindowsHookEx(
				myhookdata[4].nType,
				myhookdata[4].hkprc,
				(HINSTANCE)NULL,
				GetCurrentThreadId());
			break;
		case 'j':
			OutputDebugString(L"hookapp UNHOOK WH_KEYBOARD");
			UnhookWindowsHookEx(myhookdata[4].hhook);
			break;
		case 'k':
			OutputDebugString(L"hookapp HOOK WH_MOUSE");
			myhookdata[5].hhook = SetWindowsHookEx(
				myhookdata[5].nType,
				myhookdata[5].hkprc,
				(HINSTANCE)NULL,
				GetCurrentThreadId());
			break;
		case 'l':
			OutputDebugString(L"hookapp UNHOOK WH_MOUSE");
			UnhookWindowsHookEx(myhookdata[5].hhook);
			break;
		case 'm':
			OutputDebugString(L"hookapp HOOK WH_MSGFILTER");
			myhookdata[6].hhook = SetWindowsHookEx(
				myhookdata[6].nType,
				myhookdata[6].hkprc,
				(HINSTANCE)NULL,
				GetCurrentThreadId());
			break;
		case 'n':
			OutputDebugString(L"hookapp UNHOOK WH_MSGFILTER");
			UnhookWindowsHookEx(myhookdata[6].hhook);
			break;
		default:
			break;
		}
		break;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}


LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp CallWndProc HC_ACTION");
		break;
	}

	return CallNextHookEx(myhookdata[0].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HCBT_ACTIVATE:
		OutputDebugString(L"hookapp CBTProc HCBT_ACTIVATE");
		break;

	case HCBT_CLICKSKIPPED:
		OutputDebugString(L"hookapp CBTProc HCBT_CLICKSKIPPED");
		break;

	case HCBT_MINMAX:
		OutputDebugString(L"hookapp CBTProc HCBT_MINMAX");
		break;

	default:
		OutputDebugString(L"hookapp CBTProc default");
		break;
	}

	return CallNextHookEx(myhookdata[1].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI DebugProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp DebugProc HC_ACTION");
		break;
	}

	return CallNextHookEx(myhookdata[2].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp GetMsgProc HC_ACTION");
		break;
	}

	return CallNextHookEx(myhookdata[3].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if(nCode < 0)
		return CallNextHookEx(myhookdata[4].hhook, nCode, wParam, lParam);

	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp KeyboardProc HC_ACTION");
		if (lParam & 0x20000000)  //按下ALT键
		{
			OutputDebugString(L"hookapp KeyboardProc HC_ACTION [ALT] KEYDOWN");
		}
		if (lParam & 0x80000000)  //释放按下的键
		{
			OutputDebugString(L"hookapp KeyboardProc HC_ACTION KEYUP");
		}
		break;
	}

	return CallNextHookEx(myhookdata[4].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if(nCode < 0)
		return CallNextHookEx(myhookdata[5].hhook, nCode, wParam, lParam);
	
	switch (wParam)
	{
	case WM_LBUTTONDOWN:
		OutputDebugString(L"hookapp MouseProc WM_LBUTTONDOWN");
		PMOUSEHOOKSTRUCT pInfo = (PMOUSEHOOKSTRUCT)lParam;
		WCHAR buf[30] = {0};
		wsprintf(buf, L"hookapp (%d, %d)", pInfo->pt.x, pInfo->pt.y);
		OutputDebugString(buf);
		break;
	}

	return CallNextHookEx(myhookdata[5].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI MessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case MSGF_DIALOGBOX:
		OutputDebugString(L"hookapp MessageProc MSGF_DIALOGBOX");
		break;

	case MSGF_MENU:
		OutputDebugString(L"hookapp MessageProc MSGF_MENU");
		break;

	default:
		OutputDebugString(L"hookapp MessageProc default");
		break;
	}

	return CallNextHookEx(myhookdata[6].hhook, nCode, wParam, lParam);
}

 

或者将hook封装到dll中

 

#pragma once
#include <Windows.h>

extern "C"
{
	__declspec(dllexport) void __stdcall SetHookById(int id);

	__declspec(dllexport) void __stdcall UnHookById(int id);

	__declspec(dllexport) LRESULT WINAPI MouseProcEx(int nCode, WPARAM wParam, LPARAM lParam);

	__declspec(dllexport) void __stdcall SetHookVal(HHOOK hookval);
}
/*
内容:挂钩指定线程
 */
#include "HookMsg.h"
#include <Windows.h>
#include <Tlhelp32.h>

typedef struct _MYHOOKDATA
{
	int nType;
	HOOKPROC hkprc;
	HHOOK hhook;
}MYHOOKDATA;

/*
1、语法写错,注意空格
2、共享节的名字过长,长度不能超过32个字符
3、共享节中的数据都要初始化
 */
#pragma data_seg("shared")
MYHOOKDATA myhookdata[7] = {0};
HHOOK g_hHook = 0;
#pragma data_seg()

#pragma comment(linker, "/section:shared,rws")

LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI CBTProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI DebugProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI MouseProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI MessageProc(int nCode, WPARAM wParam, LPARAM lParam);


BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD ul_reason_for_call,
	LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		OutputDebugString(L"hookapp DLL_PROCESS_ATTACH");
		myhookdata[0].nType = WH_CALLWNDPROC;
		myhookdata[0].hkprc = CallWndProc;
		myhookdata[1].nType = WH_CBT;
		myhookdata[1].hkprc = CBTProc;
		myhookdata[2].nType = WH_DEBUG;
		myhookdata[2].hkprc = DebugProc;
		myhookdata[3].nType = WH_GETMESSAGE;
		myhookdata[3].hkprc = GetMsgProc;
		myhookdata[4].nType = WH_KEYBOARD;
		myhookdata[4].hkprc = KeyboardProc;
		myhookdata[5].nType = WH_MOUSE;
		myhookdata[5].hkprc = MouseProc;
		myhookdata[6].nType = WH_MSGFILTER;
		myhookdata[6].hkprc = MessageProc;
		break;
	case DLL_PROCESS_DETACH:
		break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
		break;
	}
	return TRUE;
}

void __stdcall SetHookById(int id)
{
	OutputDebugString(L"hookapp SetHookById start");
	myhookdata[id].hhook = SetWindowsHookEx(
		myhookdata[id].nType,
		myhookdata[id].hkprc,
		(HINSTANCE)NULL,
		GetCurrentThreadId()
	);
	OutputDebugString(L"hookapp SetHookById end");
}

void __stdcall UnHookById(int id)
{
	UnhookWindowsHookEx(myhookdata[id].hhook);
}


LRESULT WINAPI MouseProcEx(int nCode, WPARAM wParam, LPARAM lParam)
{
	WCHAR buf2[30] = { 0 };
	
	if (nCode < 0)
		return CallNextHookEx(g_hHook, nCode, wParam, lParam);

	switch (wParam)
	{
	case WM_LBUTTONDOWN:
		OutputDebugString(L"hookapp MouseProc WM_LBUTTONDOWN");
		PMOUSEHOOKSTRUCT pInfo = (PMOUSEHOOKSTRUCT)lParam;
		WCHAR buf[30] = { 0 };
		wsprintf(buf, L"hookapp (%d, %d)", pInfo->pt.x, pInfo->pt.y);
		OutputDebugString(buf);
		wsprintf(buf2, L"hookapp g_hHook = %x", g_hHook);
		OutputDebugString(buf2);
		break;
	}

	return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

void __stdcall SetHookVal(HHOOK hookval)
{
	g_hHook = hookval;
}


LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp CallWndProc HC_ACTION");
		break;
	}

	return CallNextHookEx(myhookdata[0].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HCBT_ACTIVATE:
		OutputDebugString(L"hookapp CBTProc HCBT_ACTIVATE");
		break;

	case HCBT_CLICKSKIPPED:
		OutputDebugString(L"hookapp CBTProc HCBT_CLICKSKIPPED");
		break;

	case HCBT_MINMAX:
		OutputDebugString(L"hookapp CBTProc HCBT_MINMAX");
		break;

	default:
		OutputDebugString(L"hookapp CBTProc default");
		break;
	}

	return CallNextHookEx(myhookdata[1].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI DebugProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp DebugProc HC_ACTION");
		break;
	}

	return CallNextHookEx(myhookdata[2].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp GetMsgProc HC_ACTION");
		break;
	}

	return CallNextHookEx(myhookdata[3].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if (nCode < 0)
		return CallNextHookEx(myhookdata[4].hhook, nCode, wParam, lParam);

	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp KeyboardProc HC_ACTION");
		if (lParam & 0x20000000)  //按下ALT键
		{
			OutputDebugString(L"hookapp KeyboardProc HC_ACTION [ALT] KEYDOWN");
		}
		if (lParam & 0x80000000)  //释放按下的键
		{
			OutputDebugString(L"hookapp KeyboardProc HC_ACTION KEYUP");
		}
		break;
	}

	return CallNextHookEx(myhookdata[4].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if (nCode < 0)
		return CallNextHookEx(myhookdata[5].hhook, nCode, wParam, lParam);

	switch (wParam)
	{
	case WM_LBUTTONDOWN:
		OutputDebugString(L"hookapp MouseProc WM_LBUTTONDOWN");
		PMOUSEHOOKSTRUCT pInfo = (PMOUSEHOOKSTRUCT)lParam;
		WCHAR buf[30] = { 0 };
		wsprintf(buf, L"hookapp (%d, %d)", pInfo->pt.x, pInfo->pt.y);
		OutputDebugString(buf);
		break;
	}

	return CallNextHookEx(myhookdata[5].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI MessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case MSGF_DIALOGBOX:
		OutputDebugString(L"hookapp MessageProc MSGF_DIALOGBOX");
		break;

	case MSGF_MENU:
		OutputDebugString(L"hookapp MessageProc MSGF_MENU");
		break;

	default:
		OutputDebugString(L"hookapp MessageProc default");
		break;
	}

	return CallNextHookEx(myhookdata[6].hhook, nCode, wParam, lParam);
}

在app中使用:

//TestHookMsg.cpp: 定义应用程序的入口点。
//
/*
内容:挂钩指定线程
*/

#include "stdafx.h"
#include "TestHookMsg.h"
#include <tlhelp32.h>   //快照API头文件

#define MAX_LOADSTRING 100

// 全局变量: 
HMODULE g_hModule = NULL;
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

typedef void (WINAPI *SUHF)(int id);
typedef void (WINAPI *SHV)(HHOOK hookval);
SUHF shf = NULL;
SUHF uhf = NULL;
SHV shv = NULL;
HOOKPROC pfn = NULL;
HHOOK g_hHook;
DWORD g_ulThreadId = 0;

// 此代码模块中包含的函数的前向声明: 
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
DWORD GetMainThreadIdFromName(WCHAR* szName);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此放置代码。
	

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_TESTHOOKMSG, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化: 
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTHOOKMSG));

    MSG msg;

    // 主消息循环: 
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTHOOKMSG));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_TESTHOOKMSG);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释: 
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}


// 由进程名获取主线程ID(需要头文件tlhelp32.h)
// 失败返回0
// 功能:通过名称获取主线程ID
DWORD GetMainThreadIdFromName(WCHAR* szName)
{
	DWORD idThread = 0;         // 主线程ID
	DWORD idProcess = 0;        // 进程ID

	PROCESSENTRY32 pe;      // 进程信息
	pe.dwSize = sizeof(PROCESSENTRY32);
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // 获取系统进程列表
	if (Process32First(hSnapshot, &pe))      // 返回系统中第一个进程的信息
	{
		do
		{
			if (0 == _wcsicmp(pe.szExeFile, szName)) // 不区分大小写比较
			{
				idProcess = pe.th32ProcessID;
				break;
			}
		} while (Process32Next(hSnapshot, &pe));      // 下一个进程
	}
	CloseHandle(hSnapshot); // 删除快照
	if (idProcess == 0)
	{
		return 0;
	}

	// 获取进程的主线程ID
	THREADENTRY32 te;       // 线程信息
	te.dwSize = sizeof(THREADENTRY32);
	hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); // 系统所有线程快照
	if (Thread32First(hSnapshot, &te))       // 第一个线程
	{
		do
		{
			if (idProcess == te.th32OwnerProcessID)      // 认为找到的第一个该进程的线程为主线程
			{
				idThread = te.th32ThreadID;
				break;
			}
		} while (Thread32Next(hSnapshot, &te));           // 下一个线程
	}
	CloseHandle(hSnapshot); // 删除快照
	return idThread;
}


//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
WCHAR buf2[30] = { 0 };
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            //分析菜单选择: 
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;

	case WM_CHAR:
		switch (wParam)
		{
		case 'a':
			OutputDebugString(L"hookapp HOOK WH_CALLWNDPROC");
			shf(0);
			break;
		case 'b':
			OutputDebugString(L"hookapp UNHOOK WH_CALLWNDPROC");
			uhf(0);
			break;
		case 'c':
			OutputDebugString(L"hookapp HOOK WH_CBT");
			shf(1);
			break;
		case 'd':
			OutputDebugString(L"hookapp UNHOOK WH_CBT");
			uhf(1);
			break;
		case 'e':
			OutputDebugString(L"hookapp HOOK WH_DEBUG");
			shf(2);
			break;
		case 'f':
			OutputDebugString(L"hookapp UNHOOK WH_DEBUG");
			uhf(2);
			break;
		case 'g':
			OutputDebugString(L"hookapp HOOK WH_GETMESSAGE");
			shf(3);
			break;
		case 'h':
			OutputDebugString(L"hookapp UNHOOK WH_GETMESSAGE");
			uhf(3);
			break;
		case 'i':
			OutputDebugString(L"hookapp HOOK WH_KEYBOARD");
			shf(4);
			break;
		case 'j':
			OutputDebugString(L"hookapp UNHOOK WH_KEYBOARD");
			uhf(4);
			break;
		case 'k':
			OutputDebugString(L"hookapp HOOK WH_MOUSE");
			shf(5);
			break;
		case 'l':
			OutputDebugString(L"hookapp UNHOOK WH_MOUSE");
			uhf(5);
			break;
		case 'm':
			OutputDebugString(L"hookapp HOOK WH_MSGFILTER");
			shf(6);
			break;
		case 'n':
			OutputDebugString(L"hookapp UNHOOK WH_MSGFILTER");
			uhf(6);
			break;

		case 'x':  //加载dll
			OutputDebugString(L"hookapp load library");
			g_hModule = LoadLibrary(L"HookMsg.dll");
			shf = (SUHF)GetProcAddress(g_hModule, "SetHookById");
			uhf = (SUHF)GetProcAddress(g_hModule, "UnHookById");
			break;
		case 'y':  //释放dll
			UnhookWindowsHookEx(g_hHook);
			OutputDebugString(L"hookapp free library");
			FreeLibrary(g_hModule);
			break;

		case 'p':
			OutputDebugString(L"hookapp load library");
			g_hModule = LoadLibrary(L"HookMsg.dll");
			pfn = (HOOKPROC)GetProcAddress(g_hModule, "MouseProcEx");
			shv = (SHV)GetProcAddress(g_hModule, "SetHookVal");
			g_ulThreadId = GetMainThreadIdFromName(L"notepad.exe");
			g_hHook = SetWindowsHookEx(
				WH_MOUSE,
				pfn,
				(HINSTANCE)g_hModule,
				//GetCurrentThreadId()
				//0
				g_ulThreadId
			);
			wsprintf(buf2, L"hookapp TEST g_hHook = %x", g_hHook);
			OutputDebugString(buf2);
			shv(g_hHook);

		default:
			break;
		}
		break;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

//“关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

全局钩子

一、概述
       在 Windows  中大部分的应用程序都是基于消息机制的,它们都有一个过程函数,根据不同的消息完成不同的功能。Windows 操作系统提供的钩子机制就是用来截获和监视系统中这些消息的。按照钩子作用的范围不同,它们又可以分为局部钩子和全局钩子。局部钩子是针对某个线程的,而全局钩子则是作用于整个系统的基于消息的应用。全局钩子需要使用 DLL 文件,在 DLL 中实现相应的钩子函数。

二、实现原理
        如果创建的是全局钩子,那么钩子函数必须在一个 DLL 中。这是因为进程的地址空间是独立的,发生对应事件的进程不能调用其他进程地址空间的钩子函数。如果钩子函数是实现代码在 DLL 中,则在对应事件发生时,系统会把这个 DLL 加载到发生事件的进程地址空间中,使它能够调用钩子函数进行处理。

HHOOKWINAPISetWindowsHookEx(
int idHook,\钩子类型
HOOK PROClpfn,\回调函数地址
HINSTANCE hMod,\实例句柄
DWORD dwThreadId);\线程ID

这是安装钩子的函数

首先,idHook指明了要安装钩子的类型

全局钩子原理以及操作流程

 

其中比较重要的如WH_KEYBORAD,WH_MOUSE,就是监控其他程序如果有键盘或鼠标行为就对这个信息流进行监控,可以截取修改键盘鼠标信息。

如果要对每个程序都监控,可以使用WH_GETMESSAGE。

第二,回调函数地址,一般只需写函数名。

在截取到了相应类型的消息后,要对这一消息进行处理,这个回调函数就是用来处理消息的。

如果要写一个全局钩子,lpfn参数指向的钩子函数必须位于一个DLL中。这是因为进程的地址空间是相互隔离的,发生事件的进程不能调用其他进程地址空间的钩子函数。如果钩子函数的实现代码在DLL中,在相关事件发生时,系统会把这个DLL插入到发生事件的进程的地址空间,使它能够调用钩子函数。

第三,是钩子回调函数所在的dll的实例句柄。这个我们可以在dll入口函数获得,并保存在一个全局变量中。

第四,是线程id,用来指定要监控的线程id,全局钩子置0。

回调函数

需要在dll里写一个回调函数。回调函数形如:

全局钩子原理以及操作流程

其中,nCode参数是Hook代码,钩子函数使用这个参数来确定任务,它的值依赖Hook的类型。我们在使用到某一钩子时,具体问题具体分析。

wParam和lParam参数的值依赖于Hook代码,但是它们典型的值是一些关于发送或者接收消息的信息。

因为系统中可能会有多个钩子存在,所以要调用CallNextHookEx函数把消息传到链中下一个钩子函数。hHook参数是安装钩子时得到的钩子句柄,也就是SetWindowsHookEx的返回值。我们应该把这个返回值保存在一个全局变量比如g_hook中,供这里使用。

        但是钩子成功安装后,Windows将此DLL加载到所有接受某一类型消息的其他进程的地址空间,但是在这些进程中变量钩子句柄和,因为并没有线程为它们赋值。

        这时候,我们使用共享数据段来解决这一问题。共享数据段中的数据在所有进程中共享一块内存,这意味着在一个进程中设置了共享数据段的数据,其他进程中同一数据段的数据也会随之改变。

        要使用共享数据段,我们需要添加两处代码。在dll程序中,增加#pragma data_seg()段

 

实际使用:

		case 'y':  //释放dll
			UnhookWindowsHookEx(g_hHook);
			OutputDebugString(L"hookapp free library");
			FreeLibrary(g_hModule);
			break;

		case 'p':
			OutputDebugString(L"hookapp load library");
			g_hModule = LoadLibrary(L"HookMsg.dll");
			pfn = (HOOKPROC)GetProcAddress(g_hModule, "MouseProcEx");
			shv = (SHV)GetProcAddress(g_hModule, "SetHookVal");
			g_hHook = SetWindowsHookEx(
				WH_MOUSE,
				pfn,
				(HINSTANCE)g_hModule,
				//GetCurrentThreadId()
				0
			);
			wsprintf(buf2, L"hookapp TEST g_hHook = %x", g_hHook);
			OutputDebugString(buf2);
			shv(g_hHook);

 

dll文件中:

/*
内容:全局钩子
 */
#include "HookMsg.h"
#include <Windows.h>
#include <Tlhelp32.h>

typedef struct _MYHOOKDATA
{
	int nType;
	HOOKPROC hkprc;
	HHOOK hhook;
}MYHOOKDATA;

/*
1、语法写错,注意空格
2、共享节的名字过长,长度不能超过32个字符
3、共享节中的数据都要初始化
 */
#pragma data_seg("shared")
MYHOOKDATA myhookdata[7] = {0};
HHOOK g_hHook = 0;
#pragma data_seg()

#pragma comment(linker, "/section:shared,rws")

LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI CBTProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI DebugProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI MouseProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI MessageProc(int nCode, WPARAM wParam, LPARAM lParam);


BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD ul_reason_for_call,
	LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		OutputDebugString(L"hookapp DLL_PROCESS_ATTACH");
		myhookdata[0].nType = WH_CALLWNDPROC;
		myhookdata[0].hkprc = CallWndProc;
		myhookdata[1].nType = WH_CBT;
		myhookdata[1].hkprc = CBTProc;
		myhookdata[2].nType = WH_DEBUG;
		myhookdata[2].hkprc = DebugProc;
		myhookdata[3].nType = WH_GETMESSAGE;
		myhookdata[3].hkprc = GetMsgProc;
		myhookdata[4].nType = WH_KEYBOARD;
		myhookdata[4].hkprc = KeyboardProc;
		myhookdata[5].nType = WH_MOUSE;
		myhookdata[5].hkprc = MouseProc;
		myhookdata[6].nType = WH_MSGFILTER;
		myhookdata[6].hkprc = MessageProc;
		break;
	case DLL_PROCESS_DETACH:
		break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
		break;
	}
	return TRUE;
}


void __stdcall UnHookById(int id)
{
	UnhookWindowsHookEx(myhookdata[id].hhook);
}


LRESULT WINAPI MouseProcEx(int nCode, WPARAM wParam, LPARAM lParam)
{
	WCHAR buf2[30] = { 0 };
	
	if (nCode < 0)
		return CallNextHookEx(g_hHook, nCode, wParam, lParam);

	switch (wParam)
	{
	case WM_LBUTTONDOWN:
		OutputDebugString(L"hookapp MouseProc WM_LBUTTONDOWN");
		PMOUSEHOOKSTRUCT pInfo = (PMOUSEHOOKSTRUCT)lParam;
		WCHAR buf[30] = { 0 };
		wsprintf(buf, L"hookapp (%d, %d)", pInfo->pt.x, pInfo->pt.y);
		OutputDebugString(buf);
		wsprintf(buf2, L"hookapp g_hHook = %x", g_hHook);
		OutputDebugString(buf2);
		break;
	}

	return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

void __stdcall SetHookVal(HHOOK hookval)
{
	g_hHook = hookval;
}

详细代码工程:

dll    

hookMsg.h

#pragma once
#include <Windows.h>

extern "C"
{
	__declspec(dllexport) void __stdcall SetHookById(int id);

	__declspec(dllexport) void __stdcall UnHookById(int id);

	__declspec(dllexport) LRESULT WINAPI MouseProcEx(int nCode, WPARAM wParam, LPARAM lParam);

	__declspec(dllexport) void __stdcall SetHookVal(HHOOK hookval);
}

hookMsg.cpp

/*
讲解:袁春旭
编码:袁春旭
内容:全局钩子
 */
#include "HookMsg.h"
#include <Windows.h>
#include <Tlhelp32.h>

typedef struct _MYHOOKDATA
{
	int nType;
	HOOKPROC hkprc;
	HHOOK hhook;
}MYHOOKDATA;

/*
1、语法写错,注意空格
2、共享节的名字过长,长度不能超过32个字符
3、共享节中的数据都要初始化
 */
#pragma data_seg("shared")
MYHOOKDATA myhookdata[7] = {0};
HHOOK g_hHook = 0;
#pragma data_seg()

#pragma comment(linker, "/section:shared,rws")

LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI CBTProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI DebugProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI MouseProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI MessageProc(int nCode, WPARAM wParam, LPARAM lParam);


BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD ul_reason_for_call,
	LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		OutputDebugString(L"hookapp DLL_PROCESS_ATTACH");
		myhookdata[0].nType = WH_CALLWNDPROC;
		myhookdata[0].hkprc = CallWndProc;
		myhookdata[1].nType = WH_CBT;
		myhookdata[1].hkprc = CBTProc;
		myhookdata[2].nType = WH_DEBUG;
		myhookdata[2].hkprc = DebugProc;
		myhookdata[3].nType = WH_GETMESSAGE;
		myhookdata[3].hkprc = GetMsgProc;
		myhookdata[4].nType = WH_KEYBOARD;
		myhookdata[4].hkprc = KeyboardProc;
		myhookdata[5].nType = WH_MOUSE;
		myhookdata[5].hkprc = MouseProc;
		myhookdata[6].nType = WH_MSGFILTER;
		myhookdata[6].hkprc = MessageProc;
		break;
	case DLL_PROCESS_DETACH:
		break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
		break;
	}
	return TRUE;
}

void __stdcall SetHookById(int id)
{
	OutputDebugString(L"hookapp SetHookById start");
	myhookdata[id].hhook = SetWindowsHookEx(
		myhookdata[id].nType,
		myhookdata[id].hkprc,
		(HINSTANCE)NULL,
		//GetCurrentThreadId()
		0
	);
	OutputDebugString(L"hookapp SetHookById end");
}

void __stdcall UnHookById(int id)
{
	UnhookWindowsHookEx(myhookdata[id].hhook);
}


LRESULT WINAPI MouseProcEx(int nCode, WPARAM wParam, LPARAM lParam)
{
	WCHAR buf2[30] = { 0 };
	
	if (nCode < 0)
		return CallNextHookEx(g_hHook, nCode, wParam, lParam);

	switch (wParam)
	{
	case WM_LBUTTONDOWN:
		OutputDebugString(L"hookapp MouseProc WM_LBUTTONDOWN");
		PMOUSEHOOKSTRUCT pInfo = (PMOUSEHOOKSTRUCT)lParam;
		WCHAR buf[30] = { 0 };
		wsprintf(buf, L"hookapp (%d, %d)", pInfo->pt.x, pInfo->pt.y);
		OutputDebugString(buf);
		wsprintf(buf2, L"hookapp g_hHook = %x", g_hHook);
		OutputDebugString(buf2);
		break;
	}

	return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

void __stdcall SetHookVal(HHOOK hookval)
{
	g_hHook = hookval;
}


LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp CallWndProc HC_ACTION");
		break;
	}

	return CallNextHookEx(myhookdata[0].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HCBT_ACTIVATE:
		OutputDebugString(L"hookapp CBTProc HCBT_ACTIVATE");
		break;

	case HCBT_CLICKSKIPPED:
		OutputDebugString(L"hookapp CBTProc HCBT_CLICKSKIPPED");
		break;

	case HCBT_MINMAX:
		OutputDebugString(L"hookapp CBTProc HCBT_MINMAX");
		break;

	default:
		OutputDebugString(L"hookapp CBTProc default");
		break;
	}

	return CallNextHookEx(myhookdata[1].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI DebugProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp DebugProc HC_ACTION");
		break;
	}

	return CallNextHookEx(myhookdata[2].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp GetMsgProc HC_ACTION");
		break;
	}

	return CallNextHookEx(myhookdata[3].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if (nCode < 0)
		return CallNextHookEx(myhookdata[4].hhook, nCode, wParam, lParam);

	switch (nCode)
	{
	case HC_ACTION:
		OutputDebugString(L"hookapp KeyboardProc HC_ACTION");
		if (lParam & 0x20000000)  //按下ALT键
		{
			OutputDebugString(L"hookapp KeyboardProc HC_ACTION [ALT] KEYDOWN");
		}
		if (lParam & 0x80000000)  //释放按下的键
		{
			OutputDebugString(L"hookapp KeyboardProc HC_ACTION KEYUP");
		}
		break;
	}

	return CallNextHookEx(myhookdata[4].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if (nCode < 0)
		return CallNextHookEx(myhookdata[5].hhook, nCode, wParam, lParam);

	switch (wParam)
	{
	case WM_LBUTTONDOWN:
		OutputDebugString(L"hookapp MouseProc WM_LBUTTONDOWN");
		PMOUSEHOOKSTRUCT pInfo = (PMOUSEHOOKSTRUCT)lParam;
		WCHAR buf[30] = { 0 };
		wsprintf(buf, L"hookapp (%d, %d)", pInfo->pt.x, pInfo->pt.y);
		OutputDebugString(buf);
		break;
	}

	return CallNextHookEx(myhookdata[5].hhook, nCode, wParam, lParam);
}

LRESULT WINAPI MessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode)
	{
	case MSGF_DIALOGBOX:
		OutputDebugString(L"hookapp MessageProc MSGF_DIALOGBOX");
		break;

	case MSGF_MENU:
		OutputDebugString(L"hookapp MessageProc MSGF_MENU");
		break;

	default:
		OutputDebugString(L"hookapp MessageProc default");
		break;
	}

	return CallNextHookEx(myhookdata[6].hhook, nCode, wParam, lParam);
}

demo:testHookMsg.cpp

//TestHookMsg.cpp: 定义应用程序的入口点。
//

#include "stdafx.h"
#include "TestHookMsg.h"

#define MAX_LOADSTRING 100

// 全局变量: 
HMODULE g_hModule = NULL;
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

typedef void (WINAPI *SUHF)(int id);
typedef void (WINAPI *SHV)(HHOOK hookval);
SUHF shf = NULL;
SUHF uhf = NULL;
SHV shv = NULL;
HOOKPROC pfn = NULL;
HHOOK g_hHook;

// 此代码模块中包含的函数的前向声明: 
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_TESTHOOKMSG, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化: 
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTHOOKMSG));

    MSG msg;

    // 主消息循环: 
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTHOOKMSG));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_TESTHOOKMSG);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释: 
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
WCHAR buf2[30] = { 0 };
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择: 
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;

	case WM_CHAR:
		switch (wParam)
		{
		case 'a':
			OutputDebugString(L"hookapp HOOK WH_CALLWNDPROC");
			shf(0);
			break;
		case 'b':
			OutputDebugString(L"hookapp UNHOOK WH_CALLWNDPROC");
			uhf(0);
			break;
		case 'c':
			OutputDebugString(L"hookapp HOOK WH_CBT");
			shf(1);
			break;
		case 'd':
			OutputDebugString(L"hookapp UNHOOK WH_CBT");
			uhf(1);
			break;
		case 'e':
			OutputDebugString(L"hookapp HOOK WH_DEBUG");
			shf(2);
			break;
		case 'f':
			OutputDebugString(L"hookapp UNHOOK WH_DEBUG");
			uhf(2);
			break;
		case 'g':
			OutputDebugString(L"hookapp HOOK WH_GETMESSAGE");
			shf(3);
			break;
		case 'h':
			OutputDebugString(L"hookapp UNHOOK WH_GETMESSAGE");
			uhf(3);
			break;
		case 'i':
			OutputDebugString(L"hookapp HOOK WH_KEYBOARD");
			shf(4);
			break;
		case 'j':
			OutputDebugString(L"hookapp UNHOOK WH_KEYBOARD");
			uhf(4);
			break;
		case 'k':
			OutputDebugString(L"hookapp HOOK WH_MOUSE");
			shf(5);
			break;
		case 'l':
			OutputDebugString(L"hookapp UNHOOK WH_MOUSE");
			uhf(5);
			break;
		case 'm':
			OutputDebugString(L"hookapp HOOK WH_MSGFILTER");
			shf(6);
			break;
		case 'n':
			OutputDebugString(L"hookapp UNHOOK WH_MSGFILTER");
			uhf(6);
			break;

		case 'x':  //加载dll
			OutputDebugString(L"hookapp load library");
			g_hModule = LoadLibrary(L"HookMsg.dll");
			shf = (SUHF)GetProcAddress(g_hModule, "SetHookById");
			uhf = (SUHF)GetProcAddress(g_hModule, "UnHookById");
			break;
		case 'y':  //释放dll
			UnhookWindowsHookEx(g_hHook);
			OutputDebugString(L"hookapp free library");
			FreeLibrary(g_hModule);
			break;

		case 'p':
			OutputDebugString(L"hookapp load library");
			g_hModule = LoadLibrary(L"HookMsg.dll");
			pfn = (HOOKPROC)GetProcAddress(g_hModule, "MouseProcEx");
			shv = (SHV)GetProcAddress(g_hModule, "SetHookVal");
			g_hHook = SetWindowsHookEx(
				WH_MOUSE,
				pfn,
				(HINSTANCE)g_hModule,
				//GetCurrentThreadId()
				0
			);
			wsprintf(buf2, L"hookapp TEST g_hHook = %x", g_hHook);
			OutputDebugString(buf2);
			shv(g_hHook);

		default:
			break;
		}
		break;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

挂钩指定线程

首先获取进程名的主线程:

// 由进程名获取主线程ID(需要头文件tlhelp32.h)
// 失败返回0
// 功能:通过名称获取主线程ID
DWORD GetMainThreadIdFromName(WCHAR* szName)
{
	DWORD idThread = 0;         // 主线程ID
	DWORD idProcess = 0;        // 进程ID

	PROCESSENTRY32 pe;      // 进程信息
	pe.dwSize = sizeof(PROCESSENTRY32);
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // 获取系统进程列表
	if (Process32First(hSnapshot, &pe))      // 返回系统中第一个进程的信息
	{
		do
		{
			if (0 == _wcsicmp(pe.szExeFile, szName)) // 不区分大小写比较
			{
				idProcess = pe.th32ProcessID;
				break;
			}
		} while (Process32Next(hSnapshot, &pe));      // 下一个进程
	}
	CloseHandle(hSnapshot); // 删除快照
	if (idProcess == 0)
	{
		return 0;
	}

	// 获取进程的主线程ID
	THREADENTRY32 te;       // 线程信息
	te.dwSize = sizeof(THREADENTRY32);
	hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); // 系统所有线程快照
	if (Thread32First(hSnapshot, &te))       // 第一个线程
	{
		do
		{
			if (idProcess == te.th32OwnerProcessID)      // 认为找到的第一个该进程的线程为主线程
			{
				idThread = te.th32ThreadID;//该进程下的第一个线程id为主线程
				break;
			}
		} while (Thread32Next(hSnapshot, &te));           // 下一个线程
	}
	CloseHandle(hSnapshot); // 删除快照
	return idThread;
}

比如查看记事本,使用Process Explorer查看主线程ID

双击notepad,exe

在demo中使用:

		case 'p':
			OutputDebugString(L"hookapp load library");
			g_hModule = LoadLibrary(L"HookMsg.dll");
			pfn = (HOOKPROC)GetProcAddress(g_hModule, "MouseProcEx");
			shv = (SHV)GetProcAddress(g_hModule, "SetHookVal");
			g_ulThreadId = GetMainThreadIdFromName(L"notepad.exe");
			g_hHook = SetWindowsHookEx(
				WH_MOUSE,
				pfn,
				(HINSTANCE)g_hModule,
				//GetCurrentThreadId()
				//0
				g_ulThreadId
			);
			wsprintf(buf2, L"hookapp TEST g_hHook = %x", g_hHook);
			OutputDebugString(buf2);
			shv(g_hHook);

debug查看获取的线程id和process explorer相同 

可以看到只有记事本才可以hook

远程线程注入dll

        DLL的远程注入技术是目前Win32病毒广泛使用的一种技术。使用这种技术的病毒体通常位于一个DLL中,在系统启动的时候,一个EXE程序会将这个DLL加载至某些系统进程(如Explorer.exe)中运行。
        这样一来,普通的进程管理器就很难发现这种病毒了,而且即使发现了也很难清除,因为只要病毒寄生的进程不终止运行,那么这个DLL就不会在内存中卸载,用户也就无法在资源管理器中删除这个DLL文件,真可谓一箭双雕哉。

        远程注入:

  ·OpenProcess - 用于打开要寄生的目标进程。

HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, dwProcessId);
OpenProcess第一个参数所指定的三种权限。在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都相互独立。如果一个进程需要完成跨进程的工作的话,那么它必须拥有目标进程的相应操作权限。

在这里,PROCESS_CREATE_THREAD表示我可以通过返回的进程句柄在该进程中创建新的线程,也就是调用CreateRemoteThread的权限;

同理,PROCESS_VM_OPERATION则表示在该进程中分配/释放内存的权限,也就是调用VirtualAllocEx/VirtualFreeEx的权限;
PROCESS_VM_WRITE表示可以向该进程的地址空间写入数据,也就是调用WriteProcessMemory的权限

  ·VirtualAllocEx/VirtualFreeEx - 用于在目标进程中分配/释放内存空间。

LPVOID WINAPI VirtualAllocEx(
  __in      HANDLE hProcess,   //需要在其中分配空间的进程的句柄.
  __in_opt  LPVOID lpAddress,  //想要获取的地址区域..
  __in      SIZE_T dwSize,      //要分配的内存大小.
  __in      DWORD flAllocationType, //内存分配的类型
  __in      DWORD flProtect        //内存页保护.
);

  ·WriteProcessMemory - 用于在目标进程中写入要加载的DLL名称。

  ·CreateRemoteThread - 远程加载DLL的核心内容,用于控制目标进程调用API函数。

HANDLE CreateRemoteThread(
  HANDLE                 hProcess,  //进程句柄
  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  SIZE_T                 dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,  //函数地址
  LPVOID                 lpParameter,  //传递给函数的参数
  DWORD                  dwCreationFlags,
  LPDWORD                lpThreadId
);

虽然参数过多,但我们只需要关注三个参数即可:

HANDLE hProcess 为一个进程的句柄,我们只需要通过另一个函数OpenProcess获取即可。
LPTHREAD_START_ROUTINE lpStartAddress 为我们需要"别人EXE"执行的函数的地址,同时这个函数的返回值,参数必须符合给定的模板规格。这个稍后再讲是核心。
LPVOID                 lpParamete 传递给函数的参数,该参数值须存在于"别人EXE"的虚拟内存当中,我们可以通过VirtualAllocEx函数向EXE中申请内存空间并调用 WriteProcessMemory函数写入参数,最后再传递过去。

typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(
    LPVOID lpThreadParameter
    );
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;它是一个函数指针,
传递过去就必须符合上面的格式,LoadLibraryA/W函数的格式符合
HMODULE LoadLibraryA/W(
  LPCWSTR lpLibFileName   //DLL文件的全路径
);

模板中返回值DWORD是表示一个unsigned long值,而参数中的LPVOID则可以指向任何类型。

所以除了返回值似乎有所不同以外,其他除了名字不同,简直就很像。因此我们可以通过把LoadLibraryA/W强行转换为模板的类型(微软定义了一个类型LPTHREAD_START_ROUTINE 来代指模板)传递过去即可。

可就算我们知道我们要传递的LoadLibraryA/W函数符合了模板格式,那要怎样才知道这个函数在"别人的EXE"中的地址呢?因为我们传递的LoadLibraryA/W函数地址必须是在"别人的EXE"中的地址,因此我们不能平白无故的把自己的地址传过去。


这里有一个常识,那就是每个EXE的虚拟地址是不一样,换句话来说我在自己程序里写了一个和别人EXE里相同的代码,加载出来的内存虚拟地址完全不同。

但凡事都有例外,系统核心的函数地址在每个EXE中都是一样的,而LoadLibraryA/W正是系统核心的函数之一。其位于系统核心文件kernel32.dll之中

所以我们只要:通过GetProcAddress函数获取系统核心函数LoadLibraryA/W的地址->转换成模板格式->传递到CreateRemoteThread中便可大功告成
所以可以通过 CreateRemoteThread 在其他的进程中创建一个线程调用 LoadLibrary 加载我们准备的DLL

系统启动后,kernel32.dll 的加载基址在各个进程中是相同的,因此导出函数(LoadLibrary)的地址也相同,即自己程序空间的LoadLibrary函数地址和其他进程空间的LoadLibrary函数地址相同

  

·LoadLibrary - 目标进程通过调用此函数来加载DLL。

流程:

首先得到进程ID后,就可以调用OpenProcess来打开目标进程了:

// 打开目标进程
HANDLE hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
 PROCESS_VM_WRITE, FALSE, dwProcessID ); 

现在有必要说一下OpenProcess第一个参数所指定的三种权限。在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都相互独立。如果一个进程需要完成跨进程的工作的话,那么它必须拥有目标进程的相应操作权限。

在这里,PROCESS_CREATE_THREAD表示我可以通过返回的进程句柄在该进程中创建新的线程,也就是调用CreateRemoteThread的权限;

同理,PROCESS_VM_OPERATION则表示在该进程中分配/释放内存的权限,也就是调用VirtualAllocEx/VirtualFreeEx的权限;
PROCESS_VM_WRITE表示可以向该进程的地址空间写入数据,也就是调用WriteProcessMemory的权限。

至此目标进程已经打开,那么我们该如何来将DLL注入其中呢?在这之前,我请你看一行代码,是如何在本进程内显式加载DLL的:

HMODULE hDll = LoadLibrary( "DLL.dll" ); 
那么,如果能控制目标进程调用LoadLibrary,不就可以完成DLL的远程注入了么?的确是这样,我们可以通过CreateRemoteThread将LoadLibrary作为目标进程的一个线程来启动,这样就可以完成“控制目标进程调用LoadLibrary”的工作了。到这里,也许你会想当然地写下类似这样的代码:

DWORD dwID;
LPVOID pFunc = LoadLibraryA;
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0,
 (LPTHREAD_START_ROUTINE)pFunc, 
(LPVOID)"DLL.dll", 0, &dwID ); 

不过结果肯定会让你大失所望——注入DLL失败!

,在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。在这里,我们当作参数传入的字符串"DLL.dll"其实是一个数值,它表示这个字符串位于Virus.exe地址空间之中的地址,而这个地址在传给Target.exe之后,它指向的东西就失去了有效性。

举个例子来说,譬如A、B两栋大楼,我住在A楼的401;
那么B楼的401住的是谁我当然不能确定——也就是401这个门牌号在B楼失去了有效性,而且如果我想要入住B楼的话,
我就必须请B楼的楼长为我在B楼中安排新的住处(当然这个新的住处是否401也就不一定了)。

  由此看来,我就需要做这么一系列略显繁杂的手续——

首先在Target.exe目标进程中分配一段内存空间;
然后向这段空间写入我要加载的DLL名称;

最后再调用CreateRemoteThread。

这段代码就成了这样:

// 向目标进程地址空间写入DLL名称
DWORD dwSize, dwWritten;
dwSize = lstrlenA( lpszDll ) + 1;
LPVOID lpBuf = VirtualAllocEx( hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE );
if ( NULL == lpBuf )
{
 CloseHandle( hProcess );
 // 失败处理
}
if ( WriteProcessMemory( hProcess, lpBuf, (LPVOID)lpszDll, dwSize, &dwWritten ) )
{
 // 要写入字节数与实际写入字节数不相等,仍属失败
 if ( dwWritten != dwSize )
 {
  VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );
  CloseHandle( hProcess );
  // 失败处理
 }
}
else
{
 CloseHandle( hProcess );
 // 失败处理
}
// 使目标进程调用LoadLibrary,加载DLL
DWORD dwID;
LPVOID pFunc = LoadLibraryA;
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, 
(LPTHREAD_START_ROUTINE)pFunc, lpBuf, 0, &dwID ); 

需要说的有两点:

第一,由于我要在目标进程中为ANSI字符串来分配内存空间,所以这里凡是和目标进程相关的部分,都明确使用了后缀为“A”的API函数——当然,如果要使用Unicode字符串的话,可以换作后缀是“W”的API;
第二,在这里LoadLibrary的指针我是取的本进程的LoadLibraryA的地址,这是因为LoadLibraryA/LoadLibraryW位于kernel32.dll之中,
而Win32下每个应用程序都会把kernel32.dll加载到进程地址空间中一个固定的地址,所以这里的函数地址在Target.exe中也是有效的。
在调用LoadLibrary完毕之后,我们就可以做收尾工作了:

// 等待LoadLibrary加载完毕
WaitForSingleObject( hThread, INFINITE );
// 释放目标进程中申请的空间
VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );
CloseHandle( hThread );
CloseHandle( hProcess ); 

在此解释一下WaitForSingleObject一句。由于我们是通过CreateRemoteThread在目标进程中另外开辟了一个LoadLibrary的线程,所以我们必须等待这个线程运行完毕才能够释放那段先前申请的内存。

好了,现在你可以尝试着整理这些代码并编译运行。运行Target.exe,然后开启一个有模块查看功能的进程查看工具(在这里我使用我的July)来查看Target.exe的模块,
你会发现在注入DLL之前,Target.exe中并没有DLL.dll的存在:

 在调用了注入代码之后,DLL.dll就位于Target.exe的模块列表之中了:

/*
主题:远程注入
API:
VirtualAllocEx
VirtualFreeEx
WriteProcessMemory
CreateRemoteThread
原理说明:给指定进程新建一个线程,同时使该线程加载指定的DLL
三个工程:
A:DLL
B:当前
C:目标进程
 */

#include <Windows.h>
#include <stdio.h>

int main(void)
{
	DWORD dwProcessId = 0;
	scanf_s("%d", &dwProcessId);
	HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, dwProcessId);
	if (NULL == hProcess)
	{
		printf("OpenProcess failed...\n");
		getchar();
		return 0;
	}

	char dllName[] = "mydll.dll";//dll路径是相对目录进程的路径
	DWORD dllsize = strlen(dllName) + 1;
	DWORD dwWritten = 0;
	LPVOID lpBuf = VirtualAllocEx(hProcess, NULL, dllsize, MEM_COMMIT, PAGE_READWRITE);
	if (NULL == lpBuf)
	{
		CloseHandle(hProcess);
		printf("VirtualAllocEx failed...\n");
		getchar();
		return 0;

	}
	if (WriteProcessMemory(hProcess, lpBuf, (LPCVOID)dllName, dllsize, &dwWritten))
	{
		if (dwWritten != dllsize)
		{
			VirtualFreeEx(hProcess, lpBuf, dllsize, MEM_DECOMMIT);
			CloseHandle(hProcess);
			printf("WriteProcessMemory failed...\n");
			getchar();
			return 0;
		}
	}
	else
	{
		printf("WriteProcessMemory failed...\n");
		CloseHandle(hProcess);
		getchar();
		return 0;
	}
	DWORD dwID;
	LPVOID pFunc = LoadLibraryA;
	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, lpBuf, 0, &dwID);
	if (hThread == NULL)
	{
		printf("CreateRemoteThread failed...\n");
		VirtualFreeEx(hProcess, lpBuf, dllsize, MEM_DECOMMIT);
		CloseHandle(hProcess);
		getchar();
		return 0;
	}
	WaitForSingleObject(hThread, INFINITE);
	VirtualFreeEx(hProcess, lpBuf, dllsize, MEM_DECOMMIT);
	CloseHandle(hThread);
	CloseHandle(hProcess);
	

	getchar();
	return 0;
}

 

dll:

这里我在DLL被加载和卸载的时候调用了MessageBox,这是用来显示我的远程注入/卸载工作是否成功完成

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
		//OutputDebugString(L"");
		::MessageBox(NULL, L"dll attach", L"dll attach", MB_OK);
		break;
    case DLL_THREAD_ATTACH:
		break;
    case DLL_THREAD_DETACH:
		break;
    case DLL_PROCESS_DETACH:
		//OutputDebugString(L"");
		::MessageBox(NULL, L"dll detach", L"dll detach", MB_OK);
        break;
    }
    return TRUE;
}

 

目标进程;

简单的一个win32窗口

 

测试:

win32窗口和要注入的dll放在一起

打开win32窗口,找到进程di

启动注入程序,输入进程id:

发现,mydll被注入到该exe,启动了dll中的messagebox:

已加载该dll

 

函数转发器注入dll

首先需要一个原dll,如targetdll

修改:

使用MTD可以使得编译出来的dll可以再没有C++运行环境的系统中运行

 

一种语言的开发环境往往会附带有语言库,这些库就是对操作系统的API的包装,我们也称这些语言库为运行库

对于MSVC的运行库(CRT),按照静态/动态链接,可以分为静态版和动态版;按照调试/发布,可以分为调试版本和发布版本;按照单线程/多线程,可以分为单线程版本和多线程版本(但是目前VS中已不提供单线程版本了)

在调试模式下,使用调试运行库:多线程调试(/MTd)、多线程调试DLL(/MDd)
在发布模式下,使用发布运行库:多线程(/MT)、多线程DLL(/MD)

调试模式下运行库带d,但发布模式不带。调试与发布的区别在于,发布模式省略了程序的调试信息,简单来说就是删除了调试模式下的用于调试的内容,所以一般情况下,发布模式下生成的可执行文件的大小比调试模式下生成的要小

静态链接:多线程(/MT)、多线程调试(/MTd)
动态链接:多线程DLL(/MD)、多线程调试DLL(/MDd)

动态链接为D,静态链接为T。两者的区别在于,静态链接将程序所依赖的运行库集成到了可执行文件中,可执行文件运行时不再需要运行库;动态链接没有把程序所依赖的运行库集成到可执行文件中,可执行文件运行时需要运行库。由于静态链接将程序所依赖的运行库集成到了可执行文件中,一般情况下,生成的可执行文件的大小大于动态链接生成的

/*
内容:函数转发注入DLL
示例:无参函数转发,有参函数转发
关系结构:应用程序--->替代dll--->原dll
当前:原DLL
*/

#pragma once

__declspec(dllexport) void __stdcall hello();

__declspec(dllexport) int __stdcall add1(int a, int b);
/*
内容:函数转发注入DLL
示例:无参函数转发,有参函数转发
关系结构:应用程序--->替代dll--->原dll
 */

#include <stdio.h>
#include "targetdll.h"

void __stdcall hello()
{
	printf("\n---------------------\n");
	printf("--------------hello-------------");
	printf("\n---------------------\n");
}

int __stdcall add1(int a, int b)
{
	printf("\n---------------------\n");
	printf("--------add-----%d", a + b);
	printf("\n---------------------\n");
	return a + b;
}

模块定义文件: 

EXPORTS
	hello
	add1

应用程序:

/*
内容:函数转发注入DLL
示例:无参函数转发,有参函数转发
关系结构:应用程序--->替代dll--->原dll
当前:DLL的调用程序
*/

#include <stdio.h>
#include <Windows.h>

typedef int (FAR WINAPI *ADD1)(int a, int b);

int main(void)
{
	HMODULE hModule = LoadLibrary(L"targetdll.dll");
	if (NULL == hModule)
	{
		printf("加载失败\n");
		getchar();
		return 0;
	}

	FARPROC pfn = GetProcAddress(hModule, "hello");
	pfn();

	ADD1 pfn1 = (ADD1)GetProcAddress(hModule, "add2");
	pfn1(3, 5);

	FARPROC pfn2 = GetProcAddress(hModule, "test");
	pfn2();

	FreeLibrary(hModule);
	getchar();
	return 0;
}

替代dll

/*
编码:袁春旭
讲解:袁春旭
内容:函数转发注入DLL
示例:无参函数转发,有参函数转发
关系结构:应用程序--->替代dll--->原dll
当前:替代DLL
关键指令:#pragma comment(linker, "/export:导出函数名称=被转发的dll的名称.被转发的函数名称")
*/
#include <Windows.h>
#include <stdio.h>
#include "replacedll.h"

#pragma comment(linker, "/export:hello=targetdll1.hello")

#pragma comment(linker, "/export:add2=targetdll1.add1")

void __stdcall test()
{
	printf("\n---------------------\n");
	printf("--------------test-------------");
	printf("\n---------------------\n");
}

BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		OutputDebugString("load replacedll");
		break;
	case DLL_THREAD_ATTACH:break;
	case DLL_THREAD_DETACH:break;
	case DLL_PROCESS_DETACH:
		OutputDebugString("unload replacedll");
		break;
	}
	return TRUE;
}

 

这样应用程序调用的targedll.dll其实是replacedll.dll

远程注入DLL方法有很多种,也是很多木马病毒所使用的隐藏进程的方法,因为通过程序加载的DLL在进程管理器是没有显示的.这里介绍一种用 CreateRemoteThread 远程建立线程的方式注入DLL. 首先,我们要提升自己的权限,因为远程注入必不可免的要访问到目标进程的内存空间,如果没有足够的系统权限,将无法作任何事.下面是这个函数是用来提升我们想要的权限用的. function EnableDebugPriv: Boolean; var hToken: THandle; tp: TTokenPrivileges; rl: Cardinal; begin Result := false; //打开进程令牌环 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken); //获得进程本地唯一ID if LookupPrivilegeValue(nil, 'SeDebugPrivilege', tp.Privileges[0].Luid) then begin tp.PrivilegeCount := 1; tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; //调整权限 Result := AdjustTokenPrivileges(hToken, false, tp, SizeOf(tp), nil, rl); end; end; 关于 OpenProcessToken() 和 AdjustTokenPrivileges() 两个 API 的简单介绍: OpenProcessToken():获得进程访问令牌的句柄. function OpenProcessToken( ProcessHandle: THandle; //要修改访问权限的进程句柄 DesiredAccess: DWORD; //指定你要进行的操作类型 var TokenHandle: THandle//返回的访问令牌指针 ): BOOL; AdjustTokenPrivileges() :调整进程的权限. function AdjustTokenPrivileges( TokenHandle: THandle; // 访问令牌的句柄 DisableAllPrivileges: BOOL; // 决定是进行权限修改还是除能(Disable)所有权限 const NewState: TTokenPrivileges; { 指明要修改的权限,是一个指向TOKEN_PRIVILEGES结构的指针,该结构包含一个数组, 数据组的每个项指明了权限的类型和要进行的操作; } BufferLength: DWORD; //结构PreviousState的长度,如果PreviousState为空,该参数应为 0 var PreviousState: TTokenPrivileges; // 指向TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息 var ReturnLength: DWORD //实际PreviousState结构返回的大小 ) : BOOL; 远程注入DLL其实是通过 CreateRemoteThread 建立一个远程线程调用 LoadLibrary 函数来加载我们指定的DLL,可是如何能让远程线程知道我要加载DLL呢,要知道在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。所我们需要在远程进程的内存空间里申请一块内存空间,写入我们的需要注入DLL 的路径. 需要用到的 API 函数有: OpenProcess():打开目标进程,得到目标进程的操作权限,详细参看MSDN function OpenProcess( dwDesiredAccess: DWORD; // 希望获得的访问权限 bInheritHandle: BOOL; // 指明是否希望所获得的句柄可以继承 dwProcessId: DWORD // 要访问的进程ID ): THandle; VirtualAllocEx():用于在目标进程内存空间中申请内存空间以写入DLL的文件名 function VirtualAllocEx( hProcess: THandle; // 申请内存所在的进程句柄 lpAddress: Pointer; // 保留页面的内存地址;一般用nil自动分配 dwSize, // 欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍 flAllocationType: DWORD; flProtect: DWORD ): Pointer; WriteProcessMemory():往申请到的空间中写入DLL的文件名 function WriteProcessMemory( hProcess: THandle; //要写入内存数据的目标进程句柄 const lpBaseAddress: Pointer; //要写入的目标进程的内存指针, 需以 VirtualAllocEx() 来申请 lpBuffer: Pointer; //要写入的数据 nSize: DWORD; //写入数据的大小 var lpNumberOfBytesWritten: DWORD //实际写入的大小 ): BOOL; 然后就可以调用 CreateRemoteThread 建立远程线程调用 LoadLibrary 函数来加载我们指定的DLL. CreateRemoteThread() //在一个远程进程中建立线程 function CreateRemoteThread( hProcess: THandle; //远程进程的句柄 lpThreadAttributes: Pointer; //线程安全描述字,指向SECURITY_ATTRIBUTES结构的指针 dwStackSize: DWORD; //线程栈大小,以字节表示 lpStartAddress: TFNThreadStartRoutine; // 一个TFNThreadStartRoutine类型的指针,指向在远程进程中执行的函数地址 lpParameter: Pointer; //传入参数的指针 dwCreationFlags: DWORD; //创建线程的其它标志 var lpThreadId: DWORD //线程身份标志,如果为0, 则不返回 ): THandle; 整个远程注入DLL的具体实现代码如下: function InjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: Pointer; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin lpThreadId := 0; // 计算LoadLibraryW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'LoadLibraryW'); // 启动远程线程LoadLbraryW,通过远程线程调用创建新的线程 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 如果执行成功返回 True; if (hRemoteThread 0) then Result := true; // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end; 接下来要说的是如何卸载注入目标进程中的DLL,其实原理和注入DLL是完全相同的,只是远程调用调用的函数不同而已,这里要调用的是FreeLibrary,代码如下: function UnInjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; // 进程注入和取消注入其实都差不多,只是运行的函数不同而已 var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: PChar; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId, dwHandle: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin // 计算GetModuleHandleW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'GetModuleHandleW'); //使目标进程调用GetModuleHandleW,获得DLL在目标进程中的句柄 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 等待GetModuleHandle运行完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 获得GetModuleHandle的返回值,存在dwHandle变量中 GetExitCodeThread(hRemoteThread, dwHandle); // 计算FreeLibrary的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'FreeLibrary'); // 使目标进程调用FreeLibrary,卸载DLL hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, Pointer(dwHandle), 0, lpThreadId); // 等待FreeLibrary卸载完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 如果执行成功返回 True; if hRemoteProcess 0 then Result := true; // 释放目标进程中申请的空间 VirtualFreeEx(hRemoteProcess, pszLibFileRemote, Length(DllFullPath) + 1, MEM_DECOMMIT); // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值